Routes: multiple parameters for the application root - asp.net-mvc

I am trying to set up my MVC application with the root as follows:
mysite.com/ -> Index action
mysite.com/thingID -> to CurrentThing action
mysite.com/thingID/year -> to CurrentThing action
Action: public ActionResult CurrentThing(string thingID, int year)
I also need the regular controller/action route to work both with and without an id. ThingID is a string. I have the following routes defined:
routes.MapRoute(
"Regular",
"{controller}/{action}/{id}",
new { id = UrlParameter.Optional }
);
routes.MapRoute(
"RootSpecificRecord",
"{thingID}/{year}",
new { controller = "Things", action = "CurrentThing", year = DateTime.Now.Year }
);
routes.MapRoute(
"Root",
"",
new { controller = "Things", action = "Index" }
);
This works fine until I go to mysite.com/id/year at which point the first route treats the id as a controller and the year as an action. How can I resolve this?

The routing engine is unable to tell the difference between
/{controller}/{action}/{id} and /{ThingId}/{year}
Since your id parameter is optional, and you don't have any constraints that ThingId is an id.
You could try putting the RootSpecificRecord route first, and altering the Id so that it will only accept an integer. This will make it so it's more specific than your regular route. Assuming ofcourse that thingID is an integer, you could alter your route like this:
routes.MapRoute(
"RootSpecificRecord",
"{thingID}/{year}",
new { controller = "Things", action = "CurrentThing", year = DateTime.Now.Year },
new {thingID= #".*\d+.*" }
);
See this blog post about route constraints, if you want to create a different constraint.

Default route must be last in your route tables

Related

Identify tenant from the url in multi-tenant asp.net mvc application

I am creating a multi-tenant asp.net application. I want my url to follow
**http://www.example.com/test1/test2/**{tenantName}/{controller}/{action}
**http://www.example.com/test1/**{tenantName}/{controller}/{action}
**http://www.example.com/**{tenantName}/{controller}/{action}
Here the part of the url in bold is fixed (will not change)
{tenantName}=will be logical tenant instance.
I have followed this link
What will be the routing to handle this?
It's as simple as this:
routes.MapRoute(
"MultiTenantRoute", // Route name
"test1/test2/{tenantName}/{controller}/{action}/{id}", // URL with parameters
new { id = UrlParameter.Optional } // Parameter defaults, if needed
);
The part without braces must match. The parts inside the braces will be transfer into route data parameters. I've added an optional parameter id, as you usualy find in the controllers, but you can customize it. You can also give default values to tenantName, controller or action as usual.
Remember that routes are evaluated in the order they're registered, so you should probably register this route before any other.
EDIT after question update
You cannot specify a catch all parameter like this: {*segment} at the beginning of a route. That's not possible. ASP.NET MVC wouldn't know how many segments to include in this part, and how many to be left for the rest of the parameters in the route.
So, you need to add a route for each possible case,taking into account that the first route that matches will be used. So you'd need routes starting with extra parameters like this:
{tenanName}...
{segment1}{tenanName}...
{segment1}/{segment2}/{tenanName}...
Depending on the structre of the expected urls you may need to add constraints to ensure that the route is being correctly matched. This can be done passing a fourth parameter to thw MapRoute method. This is an anonymous class, like the deafults parameter, but the specified value for each parameter is a constraint. These constraints, on their simplest forma, are simply strings which will be used as regular expressions (regex).
If the expected URLs are extremely variable, then implement yout own routing class.
You could define the route as
routes.MapRoute(
name: "TennantRoute",
url: "test1/test2/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index"}
);
and your action must take parameter with name tenantName because you may want make some decision based on that ...for example
public ActionResult Index(string tenantName)
{
return View();
}
example : http://localhost:19802/test1/test2/PrerakT/Home/Index
Please make sure you define this path above the default route for following urls to work
http://localhost:19802/test1/test2/PrerakT/
http://localhost:19802/test1/test2/PrerakT/Home/
http://localhost:19802/test1/test2/PrerakT/Home/index
What if I want test1 and test2 to be changeable ...
routes.MapRoute(
name: "TennantRoute",
url: "{test1}/{test2}/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
and
public ActionResult Index(string tenantName, string test1, string test2)
{
return View();
}
as per your update on the question
routes.MapRoute(
name: "TennantRoute1",
url: "test1/test2/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "TennantRoute2",
url: "test1/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "TennantRoute3",
url: "{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

MVC 5: Controller's action based routing

Is there a way to have different routing based upon controller's action?
For example:
Default routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
this would make the url look like
localhost:/Home/{someaction}/{id}
if the controllers action is
public ActionResult SomeAction(int id)
{
return Content("Sup?");
}
but lets suppose I have this action
public ActionResult AnotherAction(Guid productCategoryId, Guid productId)
{
return content("Hello!");
}
if I don't have any custom routing then the route would look like
localhost:/Home/AnotherAction?productCategoryId=someGuidId&productId=someGuidId
but for this action if I want the route to look like
localhost/Home/AnotherAction/productCategoryGuidId/productGuidId
how would I do that?
I have added a custom route
routes.MapRoute(
name: "appointment",
url: "{controller}/{action}/{appointmentId}/{attendeeId}",
defaults: new {controller = "Home",action = "Index", appointmentId = "",attendeeId="" }
);
but how do I say a controller's action to use that route and not default route.
Also, I read there is attribute routing in MVC 5. Would this help in my case? How would I use it in my case?
Register your custom MapRoute before your default Route. The order of which come first counts in the table route.
Routes are applied in the order in which they appear in the RouteCollection
object. The MapRoute method adds a route to the end of the collection, which means that routes are generally applied in the order in which we add them.
Hope It will help

How to run custom routing for all urls except one special in MVC3

I have done some URL routing like for some URLs.
routes.MapRoute(
"ProductDetails",
"Product/{name}/{*other}",
new { controller = "Product", action = "Details" }
);
above code will route all urls of /Product/{name} type to /Product/Details/{parameter}. Its working fine, now i want that if i enter the url /Product/List, this must be treated via default routing.
And i don't want to create one more route for List.
Please advise.
Add constraint for name parameter (not equal to List):
routes.MapRoute(
name: "ProductDetails",
url: "Product/{name}/{*other}",
defaults: new { controller = "Product", action = "Details" },
constraints: new { name = "^(?!List$).*$" }
);
this route will not match /Product/List url
UPDATE if you also want to exclude other names: ^(?!(List|Foo|Bar)$).*$

MVC route, query parameters

Two simple mvc3 routes, username and a default catch all.
routes.MapRoute(
"Users",
"{username}",
new { controller = "User", action = "Index"}
);
routes.MapRoute(
"Default",
"{*url}",
new { controller = "Default", action = "Index" }
);
How do you make the user route accept any extra query parameters like /username?ref=facebook
This example just heads of to default route...
EDIT:
MY BAD, was a bit surprised by this as it shouldn't care about query parameters.
Solution = clean and rebuild project.
Update your first route as follow:
routes.MapRoute(
"Users",
"/username",
new { controller = "User", action = "Index"}
);
In your controller Action add a parameter "ref" so that it MVC automatically passes the query string "ref" to your controller.
Query string parameters like ?ref= are not part of the Route segment definition. For example, a route llike:
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index"}
);
Would still match a URL like: /Home/Index?ref=facebook.
So you don't have to change your routes to accommodate ad hoc Query String. Handling them in your Actions/Controller is a different story, because you will have to follow and CoC Convention over Configuration guidelines and match the Query String parameter in your Actions.
Add the parameter to the route, and don't forget to add it to your action.
I would suggest adding something to the beginning of the urls to make it a bit more specific (in case you add any other routes to your project)
Example
routes.MapRoute(
"Users",
"users/{username}/{ref}",
new { controller = "User", action = "Index", ref = UrlParameter.Optional }
);
and in your action you'd want
public ActionResult Index(ref)
{
if (string.IsNullOrEmpty(ref))
{
//TODO: add your logic here
}
}
This should accept /users/someusername/facebook.com OR /users/someusername?ref=facebook.com

Why is the wrong controller being called when I start my ASP.NET MVC2 application?

I have a controller called MetricsController with a single action method:
public class MetricsController
{
public ActionResult GetMetrics(int id, string period)
{
return View("Metrics");
}
}
I want to route calls to this controller like this:
http://mysite/metrics/getmetrics/123/24h
I've mapped an additional route in my Global.asax.cs like this:
routes.MapRoute(
"Metrics",
"{controller}/{action}/{id}/{period}",
new { controller = "Metrics", action = "GetMetrics", id = 0, period = "" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I just added this to the default template project that Visual Studio 2010 creates.
When I run the application, instead of defaulting to the HomeController, it starts in the MetricsController instead.
Why is this happening? There's nothing in the url when I start the application that matches the url pattern specified in the Metrics route.
This is all being tried in Visual Studio 2010 using the built-in web server.
Because it matches first root, of course.
Thing is - when You provide default values - they become optional. If every one of routedata values are optional and route is first - it's guaranteed that it will hit first.
Something like this should work:
routes.MapRoute(
"Metrics",
"Metrics/GetMetrics/{id}/{period}",
//assuming id, period aren't supposed to be optional
new { controller = "Metrics", action = "GetMetrics" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Remove the defaults from your Metrics route:
routes.MapRoute(
"Metrics",
"{controller}/{action}/{id}/{period}",
new { controller = #"Metrics", action = "GetMetrics"}
);
With the default values MVC is able to map to the GetMetrics action in the Metric controller pretty much any URL that you pass to it.
In short: it's using the Matrics route because it matches the Matrics route.
In long: The default route defines defaults for all the route components and all of the route components are optional. All that your Metrics route is doing is adding another optional route parameter with a default... it's basically no different from the default route because the whole route contains optional parameters.
If you want it to work, you need to differentiate your Metrics route from the default route.
E.g.
routes.MapRoute(
"Metrics",
"metrics/{action}/{id}/{period}",
new { controller = #"Metrics", action = "GetMetrics", id = 0, period = "" }
);
HTHs,
Charles
Side note:
There's nothing
in the url when I start the
application that matches the url
pattern specified in the Metrics
route.
Let's look at this from a different angle - what in the url matches the url pattern specified in the default route?

Resources