I'm trying to create an ASP.NET MVC2 route with a regular expression constraint to filter language names (like en-us, pt-br) but unfortunately it doesn't work. Have a look:
routes.MapRoute(
"Culture", // Route name
"{culture}", // URL with parameters
new { controller = "Home", action = "Culture" }, // Parameter defaults
new { culture = #"^[a-z]{2}-[a-z]{2}$" }
);
Does anyone have any idea?
Edit: The url i'm testing is http://localhost/en-us
case sensitive perhaps?
"en-US"
So you need:
new { culture = #"^[a-z]{2}-[A-Z]{2}$" }
But use this one to make it case insensitive:
new { culture = #"^[a-zA-Z]{2}-[a-zA-Z]{2}$" }
I don't know why it doesn't work in your case but here's what works:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Culture",
"{culture}",
new { controller = "Home", action = "Culture" },
new { culture = #"^[a-z]{2}-[a-z]{2}$" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Controller:
public class HomeController : Controller
{
public ActionResult Culture(string culture)
{
return View();
}
}
URL: http://example.com/en-us invokes successfully the Culture action on HomeController and passes en-us in the culture parameter.
Related
This question already has answers here:
Routing in ASP.NET MVC, showing username in URL
(2 answers)
Closed 4 years ago.
I have an Asp.Net MVC project whereby we allow our users to have public profiles.
I would like to improve the url, so that it is more friendly, and shorter.
The existing code is as follows -
public class ProfileController : Controller
{
private readonly IUserProfileService _userProfileService;
public ProfileController(IUserProfileService userProfileService)
{
this._userProfileService = userProfileService;
}
public ActionResult Index(string id)
{
//Get users profile from the database using the id
var viewModel = _userProfileService.Get(id);
return View(viewModel);
}
}
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Required for the route prefix attributes to work!
routes.MapMvcAttributeRoutes();
routes.MapRoute(
"ProfileUrlIndexActionRemoval",
"Profile/{id}",
new { controller = "Profile", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
The aforementioned code allows the following url to work (based on the default MVC routing) - www.mydomain.com/profile/john-doe
What routing do I need to implement, in order to allow the following url to work instead - www.mydomain.com/john-doe
Thanks.
This is a little tricky as you want the friendly URL in the root of the site while not conflicting with any other routes.
That would mean that if you have any other routes like About or Contact you would need to make sure that are in the route table before the friendly route to avoid route conflicts.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Required for the route prefix attributes to work!
routes.MapMvcAttributeRoutes();
routes.MapRoute(
"ProfileUrlIndexActionRemoval",
"Profile/{id}",
new { controller = "Profile", action = "Index" }
);
routes.MapRoute(
name: "Home",
url: "Home/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "About",
url: "About/{action}/{id}",
defaults: new { controller = "About", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Contact",
url: "Contact/{action}/{id}",
defaults: new { controller = "Contact", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default_Frieldly",
"{*id}",
new { controller = "Profile", action = "Index" }
);
}
}
And finally because the default route will capture all unmatched routes, you will need to take not found profiles into account.
public class ProfileController : Controller {
//...code removed for brevity
public ActionResult Index(string id) {
//Get users profile from the database using the id
var viewModel = _userProfileService.Get(id);
if(viewModel == null) {
return NotFound();
}
return View(viewModel);
}
}
By having the profile controller prefix in the original URL it made it unique so as to avoid route conflicts, but in wanting the root friendly URL, while not impossible, you see the hoops needed to jump through in order to get the desired behavior.
This is how I would do it. Register a route that matches any string after the root slash.
Note that this severely limits the routes you can use for the application since not everything matching /{id} may actually be a user ID, which is why applications will typically prefix the route with /profile or /p.
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "UserIdRoute",
url: "{id}",
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 }
);
}
public ActionResult Index(string id)
{
//Get users profile from the database using the id
var viewModel = _userProfileService.Get(id);
return View();
}
How do I map unmatched routes to the index action for that controller?
I'm using a client side router for routes like /Home/foo
routes.MapRoute(
name: "Test",
url: "{controller}/{*.}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This currently results in a 404.
Your route that you used is correct, the problem is the orders of the routes that need to be added in write format:
for example if you have some routes like:
routes.MapRoute(
name: "PreTest",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Test",
url: "{controller}/{*.}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
So it is always matched with first route PreTest. Check your routes order. It is work like a dictionary that ordered. Check this for more information.
I would create an AuthorizeAttribute to handle your case. Then you can decorate your controller with that attribute.
Here's a small example to redirect your action base on a value in the route:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class RedirectAttribute:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if(filterContext.Controller.ControllerContext.RouteData.Values.ContainsValue("Foo"))
{
//Redirect to the login for example
UrlHelper urlHelper = new UrlHelper(filterContext.HttpContext.Request.RequestContext);
string url = urlHelper.Action("actionName", "controllerName");
filterContext.Result = new RedirectResult(redirectUrl);
}
}
}
Here's how to use it in a controller:
[Redirect]
public class MyCustomController : AsyncController
{
public ActionResult Index()
{
return View();
}
public ActionResult Foo()
{
//It will redirect
return View();
}
}
I'm using ASP.NET MVC 4 with C#. I'm using areas and it's named like "Admin"
Here is my route config;
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name: "PageBySlug",
url: "{slug}",
defaults: new {controller = "Home", action = "RenderPage"},
constraints: new {slug = ".+"});
routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional},
namespaces: new[] { "Web.Frontend.Controllers.Controllers" });
}
}
I generated frontend page links like; "products/apple-iphone"
So I want to call them like this.
But the error is: The code can't get the controller / action method.
I used frontend page links like;
#Html.ActionLink(linkItem.Title, "RenderPage", routeValues: new {controller = "Home", slug = linkItem.PageSlug})
#Html.RouteLink(linkItem.Title, routeName: "PageBySlug", routeValues: new { controller = "Home", action = "RenderPage", slug = linkItem.PageSlug })
#linkItem.Title
#linkItem.Title
They are rendering url links like; http://localhost:1231/products/apple-iphone
It's like what I want. But when I click any link, asp.net mvc gives me this error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /products/apple-iphone
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1069.1
Here is my controller;
namespace Web.Frontend.Controllers
{
public class HomeController : BaseFrontendController
{
public ActionResult Index()
{
return View();
}
public ActionResult RenderPage(string slug)
{
return View();
}
}
}
So how can I catch every link request like this combined slug and turn my coded view ?
The problem is, When you request products/iphone, the routing engine don't know whether you meant the slug "products/iphone" or the controller "products" and action method "iphone".
You can write a custom route constraint to take care of this. This constraint will check whether the slug part of the urls is a valid controller or not, if yes,the controller action will be executed.
public class SlugConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
var asm = Assembly.GetExecutingAssembly();
//Get all the controller names
var controllerTypes = (from t in asm.GetExportedTypes()
where typeof(IController).IsAssignableFrom(t)
select t.Name.Replace("Controller", ""));
var slug = values["slug"];
if (slug != null)
{
if (controllerTypes.Any(x => x.Equals(slug.ToString(),
StringComparison.OrdinalIgnoreCase)))
{
return false;
}
else
{
var c = slug.ToString().Split('/');
if (c.Any())
{
var firstPart = c[0];
if (controllerTypes.Any(x => x.Equals(firstPart,
StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
}
return true;
}
return false;
}
}
Now use this route constraint when you register your custom route definition for the slug. make sure you use {*slug} in the route pattern. The * indicates it is anything(Ex : "a/b/c")(Variable number of url segments- more like a catch all)
routes.MapRoute(name: "PageBySlug",
url: "{*slug}",
defaults: new { controller = "Home", action = "RenderPage" },
constraints: new { slug = new SlugConstraint() }
, namespaces: new string[] { "Web.Frontend.Controllers.Controllers" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
, new string[] { "Web.Frontend.Controllers.Controllers" });
you can provide only this type of link
#linkItem.Title
Because Routetable find your route using Route name provided by you. so controller name and action name is not necessary.
I am working on localizing my static website in multiple languages. I've already added two resource (.resx) files, Strings.resx and Strings.es.resx.
I have a RouteConfig, like this:
var language = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
routes.MapRoute(
name: "Default",
url: "{lang}/{controller}/{action}/{id}",
constraints: new { lang = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { lang = language, controller = "app", action = "index", id = UrlParameter.Optional }
);
I also have the following Filter setup:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new LocalizationAttribute("en"), 0);
}
}
Which uses this attribute:
public class LocalizationAttribute : ActionFilterAttribute
{
private string mDefaultLanguage = "en";
public LocalizationAttribute(string defaultLanguage)
{
mDefaultLanguage = defaultLanguage;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string lang = (string)filterContext.RouteData.Values["lang"] ?? mDefaultLanguage;
if (lang != mDefaultLanguage)
{
try
{
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);
}
catch (Exception)
{
throw new NotSupportedException(string.Format("ERROR: Invalid language code '{0}'.", lang));
}
}
}
}
When I navigate to my home page with this, it defaults to the English language, and the URL looks like http://example.com/.
When I navigate to any other action, it changes the URL to: http://example.com/en-us/register, for example. If I remove the en-us from the URL, and just make it http://example.com/register, I get a 404.
Note, if I change the URL to http://example.com/es/ and http://example.com/es/register, it works as expected. I'd just like the default to be English, even when en or en-us isn't supplied.
You can add more than one route mapping since your defaults are sets to en
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home or app", action = "index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{lang}/{controller}/{action}/{id}",
constraints: new { lang = #"(\w{2})|(\w{2}-\w{2})" },
defaults: new { lang = language, controller = "app", action = "index", id = UrlParameter.Optional }
);
Have you tried adding an additional route without the language in the url, and then specifying the language in the defaults? Will need to be put in after the first route to prevent it matching everything.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { lang = "en", controller = "app", action = "index", id = UrlParameter.Optional }
);
I use this log in controller :
public ActionResult Index(LoginModel model)
{
if (model.usernam == "usernam" && model.password == "password")
{
return RedirectToAction("Index", "Home");
}
return View();
}
This RedirectToAction returns this exception: No route in the route table matches the supplied values.
In my Globa.asax I have this values which I think it may solve the problem but I don't know how
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/{id1}/", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, id1= UrlParameter.Optional} // Parameter defaults
);
}
I googled searched the web I found many suggestions, but nothing works.
Is there any solution for this??
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/{id1}/", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional,
id1 = UrlParameter.Optional
} // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
}
If you pay attention to the above code you were missing the default route without parameters.