I just began working on an application with a couple areas (basic grid master / details type system..) I'm looking into taking advantage of the nice routing features in MVC (4 specifically) and I'm "just not getting it" I presume.
Currently the only route defined is the basic one:
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Account", action = "Index", id = UrlParameter.Optional }
);
which is fine, it works with the areas we have defined, so I'm assuming it must know where the user is and route to the appropriate controller based contextually on the location / area that the user is in.. so far nice..
Now, i'm trying to set up a new route that can handle
/someController/someAction/{statusName}
and specifically something like:
/OrderManager/List/New
AND
/OrderManager/List/Viewed
where "New" is the "New" status and have the Action Signature look like:
public ActionResult List(string statusName)
i was assuming I could just add the new route below the default one identifying "statusName" instead of Id, but of course, how the H would the routing mechanism know the difference between :
/controller1/action1/15
/controller2/action2/new
I did try adding a "static" route in the form of
routes.MapRoute("Default",
"ControllerName/ControllerAction/{statusName}",
new { statusName = UrlParameter.Optional }
);
I thought I could "hiJack" just that one route and do something special with it, but to know avail, the router stops at first match?? I'm assuming that was the wrong way to address this issue anyhow..
so now I'm going through the idea of getting to something like:
/somecustomroutename/somesortValue
ex.
/OrderManagerList/viewNew
where these routes would basically be "aliases". I had thought that adding the following route would do the trick:
routes.MapRoute("Default_List",
"OrderManagerList/{statusName}",
new {controller="OrderManager", action="List", statusName= UrlParameter.Optional }
);
with the associated action on the OrderManager controller:
public ActionResult List(string statusName)
no matter what I try, the argument is null, or the "resource cannot be found"
I know the controllers need to have a corresponding View file.. but that's not the issue here, the issue is my attempt at understanding the routing..
SO my questions.. fundamentally, what am I missing about the routing in MVC (4)? even some good articles for a simpleton like myself to understand?
my understanding; define a route, and map it's "endpoint".. however, i think i'm not understanding the assumptions that the machine is making..
anyhow, let me know if further explain / edit is required..
thanks in advance.
The basic principle of routes is that they are evaluated from the top down, and the routing systems uses the first match, not the best match.
The implication of this is that you must order your routes in order of specificity, with the most specific route first and the most general route last.
With this principle in mind, let’s look at your situation. Initially, you have only the default route defined:
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Account", action = "Index", id = UrlParameter.Optional }
);
The URL pattern is "{controller}/{action}/{id}". By itself, this would match any three-segment URL, and would not match any URL that had less than three segments. However, the third input parameter is the default parameter, which defines defaults for the first and second segment and indicates that the third segment is optional. The next effect of this is to make the route match URL having 0,1, 2 or 3 segments.
Now you want to add a route where the third URL segment is mapped to a “statusName” parameter, and can handle URLs like:
OrderManager/List/New
OrderManager/List/Viewed
There are two basic approaches you can take here. You can 1) create a very specific route that will handle these two URLs only, or 2) you can try and create a more general route to handle the general case. Let’s look at the first case first. You can create a route as follows:
routes.MapRoute("", "OrderManager/List/{statusName}",
new { Controller = "OrderManager", Action = "List" });
Note that because this route is more specific than the default route, you must put this route before the default route.
If you want to have a more general route, you need to decide how this route will differ from the default route, since they both will match URLs having three segments. Let’s say you decide that the new route will accept anything that only contains letters in the third segment, leaving the default route to handle anything containing numbers. You can do this using route constraints. For example you could write a route as follows:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { id = #"^\d+$" });
routes.MapRoute(
name: "NewRoute",
url: "{controller}/{action}/{statusName}",
defaults: new { Controller = "OrderManager", Action = "List" },
constraints: new { statusName = "^[A-Za-z]+$" });
With these two routes, any three-segment URL having only letters in the third segment will put the third segment into a variable called “statusName”, whereas any URL with an integer in the third segment will put the third segment into a variable called “id”.
In any applications of real complexity, routes can get complicated, and it is very advantageous to write unit tests for your routes, to insure that you don’t screw things up when you add or modify a route.
For good references on routing, see Scott Sanderson's book or see the MSDN documentation
Related
I'm looking to do something similar to this post:
How to hide controller name in Url?
only without any sort of ID.
The server is running IIS 6 and the pages already show up without extensions so it's not a wildcard issue.
I'm looking to hit http://website.com/action-name
I have http://website.com/controller/action-name working
I'm assuming this is just a simple routing change that I am somehow goofing up. My current routing rule is:
routes.MapRoute(
"RouteName",
"{action}",
new { controller = "Home", action = "Index" }
);
Is your new routing rule positioned above the default routing rule of {controller, action, id} so that it has the opportunity to match first?
The problem is your default route is still probably in place so it is matching it first and defaulting the rest of the inputs it expects. Based on your comment that the controller/action is working makes me think you didn't remove it or it is appearing first. Can you post your entire RegisterRoutes?
Try making the route you defined the very first route and it should match almost anything you pass at it.
EDIT: Added what your RegisterRoutes should look like:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// This will match anything so if you have something very specific with hard coded
// values or more items that will need to be match add them here above but do not
// add defaulted values so it can still fall through to this.
routes.MapRoute(
"RouteName",
"{action}",
new { controller = "Home", action = "Index" });
}
My ASP.NET MVC site allows users to register and give themselves user names, which will be unique and allow others to browse their pages with a clean URL that includes their name, like Twitter, Facebook, LinkedIn etc. do.
For example:
mysite.com/michael.guthrie
mysite.com/john
mysite.com/john/images
mysite.com/john/blog
etc.
The problem is that the first URL segment might be used for other "regular" controllers/actions, like:
mysite.com/about
mysite.com/register
So basically I seek for a routing scheme that says something like: If the first URL segment is a known controller, treat it as a controller (and parse the relevant action and parameters as usual), but if not - treat it as a user name, and pass it to a dedicated controller+action which will parse it and continue accordingly.
I don't want a solution that will enforce me to add routes for every specific controller that I have, such that after the routing module will go over all of them and won't find a match, it will get to the last one which defines a route for this special user name segment. The reason is primarily maintenance (I must remember to add a route every time I code a new controller, for example.)
I assume I can implement my own MvcRouteHandler / IRouteHandler but I feel there must be simpler solution that won't have me tweak MVC's out-of-the-box routing mechanism.
Note: I've read How to achieve nice litle USER page url like facebook or twitter? and it doesn't answer my question, it's just says that there is a URL rewriting module.
Do you know any good, elegant, clean way to achieve that?
You should have your first route be your Usesr route, with a route constraint along the lines of what I described in this answer: MVC routing question.
If your route is in the form {username}/{controller}/{id}, this route should cover all contingencies.
in the global.asax file you can map your routes
in the registerRoutes() method you can do something like this:
routes.MapRoute(
"ToonStudenten", // Route name
"{controller}/{action}/{userID}, // URL with parameters
new { controller = "Docent", action = "ToonStudenten", userID = UrlParameter.Optional} // Parameter defaults
);
I believe you can change the way your views look with this mapRouting, not entirely sure how though.. will try and search it up
You may want to take a look at this post:
MVC 3 keeping short url
You don't need to set a route for each URL. With a little help from route constraints you can do something like this:
routes.MapRoute(
"Home", // Route name
"{action}", // URL with parameters
new { controller = "Home", action = "Index" }, // Parameter defaults
new { action = "TaskA|TaskB|TaskC|etc" } //Route constraints
);
routes.MapRoute(
"Account", // Route name
"{action}", // URL with parameters
new { controller = "Account", action = "Logon" }, // Parameter defaults
new { action = "Logon|Logoff|Profile|FAQs|etc" } //Route constraints
);
I am trying to define dynamic sections of my site with the root url of the site. I am having some trouble defining the right MVC Route for it. Can someone please help.
My desired url will look like this: http://website.com/[dynamic-string]
But I have other standard pages like: http://website.com/about or http://website.com/faq or even just http://website.com.
My routes don't work correctly with that dynamic string. As shown below.
This is the route for the dynamic-string.
routes.MapRoute(
"CommunityName", // Route name
"{communityName}", // URL with parameters
new { controller = "Community", action = "Community", communityName = UrlParameter.Optional }
);
This is the route for all other STANDARD PAGES
routes.MapRoute(
"Default", // Route name
"{action}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
My routes just don't match up. Everything either gets diverted to one or the other route depending on which route is declared first.
There is no difference between the two routes you mention. How can MVC know which url should be mapped to communityName and which to action? Any url can match both.
You can define your standard pages as a route (before the CommunityName route) or you can catch them in your Community action, see if the name matches a function in your Home controller and then call the right action function.
I've never done this before but you might be able to create a more intelligent routehandler that looks at your controller actions, checks if the action really exists and if true selects that route.
this is beacuse the routes are effectively the same. When you declare the action route you do not state any constraints to the route, for this reason anything will be assumed to be a the action name.
If you want two routes to capture at the same level then you must constrain the action names to those that exist on your controller, this way if it does not match it will pass to the next route.
You can see an example of and advanced constraint here:
http://blogs.planetcloud.co.uk/mygreatdiscovery/post/Custom-route-constraint-to-validate-against-a-list.aspx
One of major hurdles I seem to be having recently is getting my head around some of the more complex routing requirements for some MVC based applications I've been developing.
I'm having problems finding the right set of tutorials to walk me through it to get a complete understanding.
What I'd like to find is a complete set of tutorials for everything routing from basic (controller/action/id) to advanced.
An example of what I'm calling advanced routing is things like:
/blog/year/month/day/title - would map to controller: blog and action: post and as parameters: year, month, day and title
/blog/title - would map to controller: blog and action: post and as parameters: title
/title - would map to controller: blog and action: post and as parameters: title
I could map each possible set to an explicit route in global using a database, but that seems like it's defeating the point of having the routing engine route to the correct place. I'd rather define the rule once.
I don't understand, why can't you just define each one of them as a separate route, using regular expression when needed. For example to differentiate between the /blog/year/month/day/title and /blog/title.
Each one of those sets is a separate case, and you'll need to tell MVC what to do with each one. You can do this by defining the rule once in the Global.asax.cs file:
For the first case: /blog/year/month/day/title
routes.MapRoute(
"Blog Full Route", // Route name
"blog/{year}/{month}/{day}/{title}", // URL with parameters
new {controller = "blog", action = "post"}, // Defaults
new {year = #"\d+", month= #"\d+", day = #"\d+"} // Constrain parameters with RegEx patterns
);
For second case: /blog/title
routes.MapRoute(
"Blog Title Route", // Route name
"blog/{title}", // URL with parameters
new {controller = "blog", action = "post"}, // Defaults
);
For last case: /title
routes.MapRoute(
"Title Route", // Route name
"{title}", // URL with parameters
new {controller = "blog", action = "post"}, // Defaults
);
The trick is putting theses routes in this exact order, with the least specific at the bottom. Changing the order would result in the wrong route being used (specifically in the last two). If the last case was switched with the second case, URLS of the type blog/SomeTitle would route to the post action with blog as the title.
Whenever you're creating a route for something, keep the following in mind:
Constraint route parameters with RegEx
Be very aware of route order (which route comes before which)
The squiggly brackets {something} denote action parameters
Some good tutorials:
http://www.asp.net/(S(pdfrohu0ajmwt445fanvj2r3))/learn/mvc/tutorial-24-cs.aspx
http://blogs.microsoft.co.il/blogs/bursteg/archive/2009/01/11/asp-net-mvc-route-constraints.aspx
http://blogs.microsoft.co.il/blogs/bursteg/archive/2008/12/21/anatomy-of-an-asp-net-mvc-application.aspx
I'm having some issues trying to setup my Routing in MVC. I think I understand how it works, but I just can't seem to set the proper paths.
Basically I want to do something similar to how StackOverflow works so:
http://localhost/faq
I want this to grab the HomeController, hit the faq action and return the faq view. I can't seem to figure out how to do this.
Also, I tried adding a new route for something like this:
http://localhost/Boxes/25
So, Boxes is the controller, 25 is obviously the id(parameter). Similar to how stackoverflow has: https://stackoverflow.com/questions/[question number]/[question title]
So I tried doing this:
routes.MapRoute(
"Boxes",
"Boxes/{boxnumber}",
new {
action="Details", cubenumber = ""
}
);
with no success.
I've also downloading the Route Tester app, but that doesn't seem to be helping at this point. Most likely I need to really read up on how routing works, but was just wondering if someone could point me in the right direction right now instead of me having to spin my wheels.
Thanks a lot guys!
Try the following:
routes.MapRoute(
null, // optional route name
"faq",
new { controller="Home", action="Faq" } );
routes.MapRoute(
null, // optional route name
"Boxes/{boxnumber}",
new { controller="Boxes", action="Details", boxnumber = ""} );
// Original route, if needed, should come AFTER more specialized routes.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } ); // Parameter defaults
Some notes that may help you understand this better:
the controller and action parameters must be specified, either explicitly in the incoming URL or via defaults that you specify (if missing in incoming URL)
the order of adding routes is significant because the first match will be used for each incoming URL. In the above example, if the original route is added first, the other ones will never be matched (because the original route specifies defaults for all parameterized parts of the URL)
route name is optional, only needed if you are using route names to generate outbound URLs
When you define a route, it has to at minimum contain two pieces of information: a controller and an action. Those values can either come through as a parameter (i.e. a "{parameter}" portion in the URL pattern), or as a default value.
The route example that you pasted above includes an action but it doesn't contain a controller, so it isn't capable of satisfying a request. Since your controller name is "BoxesController", you could simply add "controller='Boxes'" to the default values of that route and you'd be good.
To achieve the faq route, you could simply define a route whose URL was "faq" and had the default values: controller="Home", action="Faq".