I created this route
routes.MapRoute(
name: "Survey",
url: "{controller}/{action}/{surveyid}/{userid}/{hash}",
defaults: new { controller = "Home", action = "Survey" },
constraints: new { surveyid = #"\d+", userid = #"\d+" }
);
When I then browse to
http://localhost:3086/Home/Survey/1/1/3r2ytg
It works, however if I browse to
http://localhost:3086/1/1/3r2ytg
it does not work.
If I then changed the route like so
routes.MapRoute(
name: "Survey",
url: "{surveyid}/{userid}/{hash}",
defaults: new { controller = "Home", action = "Survey" },
constraints: new { surveyid = #"\d+", userid = #"\d+" }
);
The exact opposite would work (and that makes sense).
But I am curious with the first route i thought both URLs should work since it shoudl grab the default controller and action when none is given.
Update
In the end I went with only this
routes.MapRoute(
name: "Survey",
url: "{surveyId}/{userId}/{hash}",
defaults: new { controller = "Home", action = "Survey" },
constraints: new { surveyId = #"\d+", userId = #"\d+" }
);
as that is the behavior I wanted. However when I then call
#Url.Action("Survey", "Home", new
{
userId = #Model.UserId,
surveyId = survey.Id,
hash = HashHelpers.CreateShortenedUrlSafeHash(#Model.SecretString + survey.Id.ToString() + #Model.UserId.ToString())
})
It generates
/Admin/Home/Survey?userId=25&surveyId=19&hash=2Norc
instead of a shiny path. I can force it with Url.RouteUrl but I thought it should have picked this one automatically.
You need to create route for each combination.
Check this Phil Haack Article
routes.MapRoute(
name: "Survey",
url: "{controller}/{action}/{surveyid}/{userid}/{hash}",
defaults: new { controller = "Home", action = "Survey" },
constraints: new { surveyid = #"\d+", userid = #"\d+" }
);
routes.MapRoute(
name: "Survey",
url: "{surveyid}/{userid}/{hash}",
defaults: new { controller = "Home", action = "Survey" },
constraints: new { surveyid = #"\d+", userid = #"\d+" }
);
Check this Route with Two optional parameters in MVC3 not working
The routehandler doesn't really know that if you say /1/1/3r2ytg the 1 is for surveyId, the other 1 is for userId etc.
It just knows a path (localhost:3086) and x amount of "folders"
So if you call http://localhost:3086/1/1/3r2ytg he will map 1 to controller, 1 to action and 3r2ytg to surveyId.
It can't find userId or hash and since there are no defaults specified he can't find the route.
The defaults in your first route are pointless since they will never trigger.
Default values should be at the end of your url, which kinda makes sense.
Related
I have custom routing for controller added right over the default one:
Custom:
routes.MapRoute(
name: "FaqSubCategory",
url: "{culture}/{controller}/{action}/{TapCode}",
defaults: new { controller = "FAQ", action = "GetChosenFaqSubCategory", TapCode = UrlParameter.Optional },
constraints: new { culture = new CultureConstraint() }
);
Custom v2:
routes.MapRoute(
name: "FaqSubCategory",
url: "{culture}/{controller}/{action}/{TapCode}",
defaults: new { controller = "FAQ", action = "GetChosenFaqSubCategory", TapCode = UrlParameter.Optional },
constraints: new { culture = new CultureConstraint(), FAQ = new TranslateControllersConstraint("4189") }
);
Default:
routes.MapRoute(
name: "DefaultWithCulture",
url: "{culture}/{controller}/{action}/{ID}",
defaults: new { controller = "Home", action = "Index", ID = UrlParameter.Optional },
constraints: new { culture = new CultureConstraint() }
);
Basically because of that custom route, home page is receiving "/home/index" and some of the other pages are receiving "/index" at the end of the URL
When I write the custom routing like the second variant, then everything is working perfectly except the FAQ controller.
When an MVC application first starts it creates the route table.The default route table contains a single route named Default. So default route should have Default name, instead of DefaultWithCulture an your default route should have default culture
routes.MapRoute(
name: "Default",
url: "{culture}/{controller}/{action}/{ID}",
defaults: new { culture="en", controller = "Home", action = "Index", ID = UrlParameter.Optional },
.....
I am working with internationalization and long routes.
My URLs look like domain/en-us/users/edit/240.
Here is my RouteConfig :
routes.MapRoute(
name: "DefaultWithCulture",
url: "{culture}/{controller}/{action}/{id}",
defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { culture = "([a-zA-Z]{2}-[a-zA-Z]{2})|[a-zA-Z]{2}" }
);
routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And here is some Actions in my Controller Users :
public ActionResult Edit(int id);
public ActionResult EditPassword(int id);
public ActionResult EditRights(int id);
I want my Actions EditPassword and EditRights to be accessible via domain/en-us/users/edit/password/240 and domain/en-us/users/edit/rights/240.
I did find a solution, using Route Attributes but with the culture (en-us, es-mx, fr-ca) in my url, it broke it all.
The DefaultWithCulture route you have configured will match 3 or 4 route segments. Your URL (en-us/users/edit/password/240) has 5 route segments, so it will not match.
It will match if you pass it the action method name as you have it configured: en-us/users/editpassword/240.
If you want your URLs to look like your example with 5 segments, you will need to do some additional work. First of all, you need to account for the fact that your action names and URLs don't match. One way to do that is to use the ActionName attribute.
[ActionName("password")]
public ActionResult EditPassword(int id);
[ActionName("rights")]
public ActionResult EditRights(int id);
Then you need some new routes and constraints to match the 4 or 5 segment URLs. The main issue to deal with is that some of your segments overlap. So, you need a constraint so you can tell when the 4th segment is an id or if it is an action method.
Basically, to do this break the 1 route with an optional id parameter into 2 routes with required parameters (since when you add a constraint to id it makes it required).
Then add another route for both the localized and non-localized version that handles the extra edit segment in your URL.
// Route to handle culture with 4 segments, ending in numeric id
routes.MapRoute(
name: "DefaultWithCulture",
url: "{culture}/{controller}/{action}/{id}",
defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Home", action = "Index" },
constraints: new { culture = "([a-zA-Z]{2}-[a-zA-Z]{2})|[a-zA-Z]{2}", id = #"\d+" }
);
// Route to handle culture with 3 segments, to make id optional
routes.MapRoute(
name: "DefaultWithCulture3Segments",
url: "{culture}/{controller}/{action}",
defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Home", action = "Index" },
constraints: new { culture = "([a-zA-Z]{2}-[a-zA-Z]{2})|[a-zA-Z]{2}" }
);
// Route to handle culture with 4 or 5 segments
routes.MapRoute(
name: "DefaultWithCulture5Segments",
url: "{culture}/{controller}/edit/{action}/{id}",
defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { culture = "([a-zA-Z]{2}-[a-zA-Z]{2})|[a-zA-Z]{2}" }
);
// Route to handle default with 3 segments, ending in numeric id
routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { id = #"\d+" }
);
// Route to handle default with 2 segments, to make id optional
routes.MapRoute(name: "Default2Segments",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
// Route to handle default with 3 or 4 segments
routes.MapRoute(name: "Default4Segments",
url: "{controller}/edit/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you want the extra segment you added to read something other than edit, you will need additional routes because MVC doesn't understand this URL scheme natively.
I have an web app build with asp.net MVC 4.
I want to have the following 3 types of routes:
/action
/action/id
/id/id2
In global.asax I have changed the routes as it follows:
routes.MapRoute(
name: "Without Action",
url: "{id}/{id2}",
defaults: new { controller = "Home", action = "City_Category" },
namespaces: new[] { "Namespace.Controllers" }
);
routes.MapRoute(
name: "Without Controller",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Namespace.Controllers" }
);
But when I try an {action}/{id} it goes to the first route defined in global.asax. Works only if url is {action} or {id}/{id2}.
How can I make to work all 3 routes?
Thanks!
If {id} and {id2} will always be numeric, you can add a constraint to the route so that it will only kick in when those values are digits:
routes.MapRoute(
name: "Without Action",
url: "{id}/{id2}",
defaults: new { controller = "Home", action = "City_Category" },
new { id = #"\d+", id2 = #"\d+" }
namespaces: new[] { "Namespace.Controllers" }
);
Another idea is to use http://attributerouting.net/, you can define routes by attributes, which is a very easy to define routes, especially if you end up with a lot of complex routes. I had about 30 route definitions and a lot of comments how the url looks like for each action. I could remove all comments and route definitions with these attributes.
I'm adding a new route into my mvc web application and it's not working as desired. I was hoping someone could help me figure out where in the list it should go and maybe which defaults and/or constraints should be defined for it (in RouteConfig.cs).
The desired route would look like so:
/controller/id/slug/action e.g. mydomain.com/products/10/product-name/reviews
I've tried to define this route like so and have tried it as the 1st, 2nd and 3rd routes listed:
routes.MapRoute(
name: "AlternateRoute",
url: "{controller}/{id}/{slug}/{action}",
defaults: null,
constraints: new { id = #"\d+", slug = #"[\w\-\d+]*" }
);
What's happening is after I add the above route, and browse to a page like /products/10/product-name - url's that were previously something like /products/create look like /products/10/product-name/create (but only on that page).
The only other routes I have are these 3 (defined in my routeConfig file):
/controller/id/slug
routes.MapRoute(
name: "DefaultSlugRoute",
url: "{controller}/{id}/{slug}",
defaults: new { action = "Details", slug = "" },
constraints: new { id = #"\d+", slug = #"[\w\-\d+]*" }
);
/controller/action/year/month
routes.MapRoute(
name: "MonthlyArchiveRoute",
url: "{controller}/{action}/{year}/{month}",
defaults: new { controller = "Blog", action = "Archives", year = UrlParameter.Optional, month = UrlParameter.Optional },
constraints: new { year = #"\d{4}", month = #"\d{2}" }
);
/controller/action/id (the standard one included w/ a new mvc project)
routes.MapRoute(
name: "DefaultRoute",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I am extending a model to include a link property. It is based on other properties within it to generate a link:
public partial class MyModelType
{
public string FilterLink
{
get
{
var u = new UrlHelper(HttpContext.Current.Request.RequestContext);
var route = new RouteValueDictionary(u.RequestContext.RouteData.Values);
route.Remove("other");
route.Add("other", null);
route.Add("id", this.Id);
return u.Action("Index", "ControllerName", route);
}
}
}
The link generated is /ControllerName/1?other=2. If I get rid of the route.Remove("other") I get the link based on the mapped route: /ControllerName/1/2. How can I prevent it being used as a query string parameter when it is removed from the route? The UrlHelper seems to be adding it somehow, even though it is not a route value.
Check your routing configuration.
MVCs routing will append any unkown parts of your route in that way, so as long as you define it up front it should work just fine.
You probably just have this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Try adding the option like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{option}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, option = UrlParameter.Optional }
);
The issue is discussed here: http://erraticdev.blogspot.com/2011/03/removing-route-values-from-linksurls-in.html
However, the blogs proposal is convoluted compared with a simple solution in the comments: add "other = null as object" to the defaults values on your DEFAULT route.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, other = null as object }
);