I'm trying to build two routes only to action and to controller with id, keeping the default.
I have to access:
www.mysite.com/MyController/MyAction/{OptionalId}
www.mysite.com/MyController/{OptionalId}
www.mysite.com/MyActionFromHomeController
I was able to create routes to work with first and the third point, but not for the second. Current code:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "OnlyActionToHomeController",
url: "{action}",
defaults: new { controller = "Home" },
constraints: new { noConflictingControllerExists = new NoConflictingControllerExists() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
public class NoConflictingControllerExists : IRouteConstraint
{
private static readonly Dictionary<string, bool> _cache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var path = httpContext.Request.Path;
if (path == "/" || String.IsNullOrEmpty(path))
return false;
if (_cache.ContainsKey(path))
return _cache[path];
IController ctrl;
try
{
var ctrlFactory = ControllerBuilder.Current.GetControllerFactory();
ctrl = ctrlFactory.CreateController(httpContext.Request.RequestContext, values["action"] as string);
}
catch
{
_cache.Add(path, true);
return true;
}
var res = ctrl == null;
_cache.Add(path, res);
return res;
}
}
I did!
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "OnlyController",
url: "{controller}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { id = #"\d+" } // define the id parameter needs to be integer
);
routes.MapRoute(
name: "OnlyActionToHomeController",
url: "{action}",
defaults: new { controller = "Home" },
constraints: new { noConflictingControllerExists = new NoConflictingControllerExists() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
public class NoConflictingControllerExists : IRouteConstraint
{
private static readonly Dictionary<string, bool> _cache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var path = httpContext.Request.Path;
if (path == "/" || String.IsNullOrEmpty(path))
return false;
if (_cache.ContainsKey(path))
return _cache[path];
IController ctrl;
try
{
var ctrlFactory = ControllerBuilder.Current.GetControllerFactory();
ctrl = ctrlFactory.CreateController(httpContext.Request.RequestContext, values["action"] as string);
}
catch
{
_cache.Add(path, true);
return true;
}
var res = ctrl == null;
_cache.Add(path, res);
return res;
}
}
Related
I have configured routing like this:
config.Routes.MapHttpRoute(
name: "Sales",
routeTemplate: "api/{Sales}/{jsonresponce}",
defaults: new { controller = "Sales", action = "Postsomething" }
);
config.Routes.MapHttpRoute(
name: "User",
routeTemplate: "api/{User}/{GetDetails}",
defaults: new { controller = "User", action = "GetDetails" }
);
Here is my UserController:
public class UserController : ApiController
{
userservice objservice = new userservice();
[HttpGet]
public CustDetails GetDetails(string Username, string Password, string BillingFeedID)
{
CustDetails model = new CustDetails();
//checking for encrypted password
model.UserName = Username;
model.Password = Password;
model.BillingFeedID = BillingFeedID;
model = objservice.Login(model);
//taking merchant configuration data
var data = objservice.Getcustomerconfig(model.MerchantID, BillingFeedID);
model.LastPosBillID = data.LastPosBillID;
model.LastTimeStamp = data.LastTimeStamp;
model.SyncStatus = data.SyncStatus;
model.SynsTimeInterval = data.SynsTimeInterval;
model.DataSorce = data.DataSorce;
model.DataAuthentication = data.DataAuthentication;
model.DataBaseQuery = data.DataBaseQuery;
return model;
}
}
I also have a SalesController:
public class SalesController : ApiController
{
[HttpPost]
public async Task<HttpResponseMessage> PostSomething()
{
StringBuilder sb = new StringBuilder();
try
{
string jsonData = await Request.Content.ReadAsStringAsync();
// dynamic dataList = JArray.Parse(jsonData);
if (File.Exists(#"C:\MCarrots\Umairbills\Umairbills.json"))
File.Delete(#"C:\MCarrots\Umairbills\Umairbills.json");
File.Create(#"C:\MCarrots\Umairbills\Umairbills.json").Close();
File.WriteAllText(#"C:\MCarrots\Umairbills\Umairbills.json", jsonData);
return Request.CreateResponse(HttpStatusCode.OK, "OK");
}
catch (Exception ex)
{
File.WriteAllText(#"C:\MCarrots\mcarrots\Umairbills.json", ex.ToString());
return Request.CreateResponse(HttpStatusCode.NoContent, ex.ToString());
}
}
When I try to call the GetUserDetails action with this url:
http://localhost:42945/api/User/GetDetails?Username=kay001&Password=kay501&BillingFeedID=KF1
It is throwing this error:
"Message":"The requested resource does not support http method"
But the POST method in SalesController is working.
Your route templates seem off. I think they should be:
config.Routes.MapHttpRoute(
name: "Sales",
routeTemplate: "api/Sales/{action}",
defaults: new { controller = "Sales", action = "Postsomething" }
);
config.Routes.MapHttpRoute(
name: "User",
routeTemplate: "api/User/{action}",
defaults: new { controller = "User", action = "GetDetails" }
);
I changed the route templates so that the controller name is essentially hard-coded, and the action is a placeholder. The action can be left out in this case though, defaulting to GetDetails.
I would like to be able to create a succinct language-specific default URL for my website so that when someone browses to:
somesite.com
They get redirected to a language-culture page such as:
somesite.com/en-US/
somesite.com/sp-MX/
somesite.com/fr-FR/
Specifically, I do not want /Home/Index appended to the URLs:
somesite.com/en-US/Home/Index
somesite.com/sp-MX/Home/Index
somesite.com/fr-FR/Home/Index
I am committed to making this site using RouteLocalization.mvc
Dresel/RouteLocalization
Translating Your ASP.NET MVC Routes
And I would like to use MVC Attribute Routing to the extent feasible.
I am having trouble figuring out how to cause the Start() method redirect to a language-culture specific URL without the addition of something like "index".
Samples of what I have attempted follow:
using RouteLocalization.Mvc;
using RouteLocalization.Mvc.Extensions;
using RouteLocalization.Mvc.Setup;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.Clear();
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);
const string en = "en-us";
ISet<string> acceptedCultures = new HashSet<string>() { en, "de", "fr", "es", "it" };
routes.Localization(configuration =>
{
configuration.DefaultCulture = en;
configuration.AcceptedCultures = acceptedCultures;
configuration.AttributeRouteProcessing = AttributeRouteProcessing.AddAsNeutralAndDefaultCultureRoute;
configuration.AddCultureAsRoutePrefix = true;
configuration.AddTranslationToSimiliarUrls = true;
}).TranslateInitialAttributeRoutes().Translate(localization =>
{
localization.AddRoutesTranslation();
});
CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = Localization.DetectCultureFromBrowserUserLanguages(acceptedCultures, en);
var defaultCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
routes.MapRoute(
name: "DefaultLocalized",
url: "{culture}/{controller}/{action}/{id}",
constraints: new { culture = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { culture = defaultCulture, controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
And my home controller:
public class HomeController : Controller
{
[HttpGet]
[Route]
public RedirectToRouteResult Start()
{
return RedirectToAction("Home", new { culture = Thread.CurrentThread.CurrentCulture.Name });
}
[Route("Index", Name = "Home.Index")]
public ActionResult Index()
{
return View();
}
public ActionResult Contact()
{
return View();
}
public ActionResult About()
{
return View();
}
}
My Global.asax file:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
}
}
Redirecting is a separate concern than routing. Since your goal of redirecting any URL to its localized counterpart is a cross-cutting concern your best bet is to make a global filter.
public class RedirectToUserLanguageFilter : IActionFilter
{
private readonly string defaultCulture;
private readonly IEnumerable<string> supportedCultures;
public RedirectToUserLanguageFilter(string defaultCulture, IEnumerable<string> supportedCultures)
{
if (string.IsNullOrEmpty(defaultCulture))
throw new ArgumentNullException("defaultCulture");
if (supportedCultures == null || !supportedCultures.Any())
throw new ArgumentNullException("supportedCultures");
this.defaultCulture = defaultCulture;
this.supportedCultures = supportedCultures;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var routeValues = filterContext.RequestContext.RouteData.Values;
// If there is no value for culture, redirect
if (routeValues != null && !routeValues.ContainsKey("culture"))
{
string culture = this.defaultCulture;
var userLanguages = filterContext.HttpContext.Request.UserLanguages;
if (userLanguages.Length > 0)
{
foreach (string language in userLanguages.SelectMany(x => x.Split(';')))
{
// Check whether language is supported before setting it.
if (supportedCultures.Contains(language))
{
culture = language;
break;
}
}
}
// Add the culture to the route values
routeValues.Add("culture", culture);
filterContext.Result = new RedirectToRouteResult(routeValues);
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Do nothing
}
}
Usage
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RedirectToUserLanguageFilter("en", new string[] { "en", "de", "fr", "es", "it" }));
filters.Add(new HandleErrorAttribute());
}
}
Note also that your routing is misconfigured. The route setup is run one time per application startup, so setting the default culture to that of the current thread is meaningless. In fact, you should not be setting a default culture at all for your culture route because you want it to miss so your Default route will execute if there is no culture set.
routes.MapRoute(
name: "DefaultLocalized",
url: "{culture}/{controller}/{action}/{id}",
constraints: new { culture = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have created a sub-domain route class in RouteConfig.cs as:
public class SubdomainRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext.Request == null || httpContext.Request.Url == null)
{
return null;
}
var host = httpContext.Request.Url.Host;
var index = host.IndexOf(".");
string[] segments = httpContext.Request.Url.PathAndQuery.TrimStart('/').Split('/');
if (index < 0)
{
return null;
}
var subdomain = host.Substring(0, index);
string[] blacklist = { "www", "yourdomain", "mail","localhost" };
if (blacklist.Contains(subdomain))
{
return null;
}
string controller = (segments.Length > 0) ? segments[0] : "Home";
if (controller == "")
controller = "Home";
string action = (segments.Length > 1) ? segments[1] : "Index";
var routeData = new RouteData(this, new MvcRouteHandler());
routeData.Values.Add("controller", controller);
routeData.Values.Add("action", action);
routeData.Values.Add("subdomain", subdomain);
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
I updated RegisterRoutes function to :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new SubdomainRoute());
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{category}/{subcategory}/{lowcategory}/{id}",
defaults: new { controller = "Home", action = "Index",category=UrlParameter.Optional,subcategory=UrlParameter.Optional,lowcategory=UrlParameter.Optional, id = UrlParameter.Optional }
);
}
My Index function is :
public string Index(string category, string subcategory, string lowcategory,int? id)
Now the link http://localhost:29808/Home/Index/mobiles/Htc/M8/3 is working fine but with subdomain its not working electronics.localhost:29808/Home/Index/mobiles/HTC/M8/3. (It shows null in category and subcategory). What else if have to include to make it work?
I want to create a Mvc route that match every string, but all those ones that contains some values, for instance, I have this route:
context.MapRoute(
"Users_Bootstrap",
"{ulrPrefix}/{*catchall}",
new { controller = "Start", action = "Index" },
namespaces: new[] { "Fanapo.Web.Areas.Users.Controllers" },
constraints: new { }
);
I want this route doesn't match any string that urlPrefix parameter be Account or Admin or ...
Something like this: ulrPrefix NOT IN [Account, Admin, ...]
I think this should be solved using regular expresions, hope IRouteConstraint be the last option. Thanks.
Solved using route constrains, based on this answer
public class NotInRouteConstraint : IRouteConstraint
{
private readonly string _notInString;
public NotInRouteConstraint(string notInString)
{
_notInString = notInString;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
if (string.IsNullOrEmpty(_notInString))
return true;
var notList = _notInString.Split('|').Select(s => s.Trim().ToLower()).ToList();
var value = values[parameterName] as string;
if (!string.IsNullOrEmpty(value))
{
return !notList.Contains(value.ToLower());
}
return true;
}
}
The usage code:
//this is in another area
context.MapRoute(
"Users_Bootstrap",
"{ulrPrefix}/{*catchall}",
new { controller = "Start", action = "Index" },
constraints: new { ulrPrefix = new NotInRouteConstraint(RouteConfig.ReservedUrlPrefix.JoinStrings("|")) }
);
Hope there is another nicer solution. Thanks.
EDIT
A bit more faster:
public class NotInRouteConstraint : IRouteConstraint
{
private readonly IEnumerable<string> _notInString;
public NotInRouteConstraint(IEnumerable<string> notInString)
{
_notInString = notInString;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
if (_notInString == null)
return true;
var value = values[parameterName] as string;
if (!string.IsNullOrEmpty(value))
{
return !_notInString.Contains(value.ToLower());
}
return true;
}
}
AND
context.MapRoute(
"Users_Bootstrap",
"{ulrPrefix}/{*catchall}",
new { controller = "Start", action = "Index" },
constraints: new { ulrPrefix = new NotInRouteConstraint(RouteConfig.ReservedUrlPrefix.Select(s=>s.Trim().ToLower())) }
);
I am trying to route an old URL that is domain.com/1024 to my home page in mvc.
I keep getting resource not found errors.
Here is my routeConfig
routes.MapRoute(
name: "1024",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Direct", id = UrlParameter.Optional }
);
My Controller
public ActionResult Direct()
{
return RedirectToAction("Index");
}
If you have your route definition before the default, for example:
routes.MapRoute(
name: "1024",
url: "{id}",
defaults: new { controller = "Home", action = "Direct" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This will route (host):(port)/ to Index and (host):(port)/1024 to Direct where you can RedirectToAction("Index").
For example a HomeController to demonstrate the point (This ONLY redirects 1024 to index):
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult Direct(int? id)
{
if (id.HasValue && id.Value == 1024)
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
}
This would redirect any id to index:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult Direct(int? id)
{
return RedirectToAction("Index");
}
}