ASP.NET MVC Default route? - asp.net-mvc

I created a new ASP.NET MVC project and implemented a site authorization filter.
When I map the routes to the {controller}/{action} pair, I pass a role = "SomeRole" default to the route.
It works perfectly if I go through the full url (http://localhost/somecontroller/someaction) and I specified the full route
MapRoute("SomeAction", "somecontroller/someaction",
new { controller = "SomeController", action = "SomeAction", role = "SomeRole");
The problem is that when somebody visits http://thesiteaddress.com there has to be a default route that invokes /home/index instead of / and if I specify
MapRoute("Default", new { controller="somecontroller",action="action" });
then I lose the role="SomeRole" from the previous MapRoute.
How can I solve this?

Make sure the Default route is at the BOTTOM of your listed route table. Order matters when it comes to ASP.NET MVC Routing tables.
The correct ordering is your 'most specific' route to your least specific route.

Actually, George is right. MVC Routing respect ordering route. Your last route must be generic as possible, and your previous route must be specific as possible.
In your case, both are generic. You should
MapRoute("SomeAction", "Post/{action}", new {controller = "Post", role = "User");
and then
MapRoute("Default", new {controller="Home", action="Index", role = "Anonymous"});
so, you give specificity to both routes.

Phil Haack released a route debugging tool that can be invaluable in gaining an understanding of problems like this.
With this tool you can view how your MVC application parses a URL and matches it to your RouteTable.

When you don't provide the route name or the action is determined through a HTTP request it will look in order from the order they were added. The first time it finds one that matches, it stops. So what's probably happening is it's matching one previous to the one you've added.

Related

Views / Controller Actions / Routes / Gobal.asax

I am new to ASP.NET MVC, coming from a web forms background in the past year and I've started working with MVC 3 recently and have questions that seem to have no good answers (none that I can find at this point) and wanted to post them here. Any help would be appreciated. My questions focus solely on controllers, controller actions, views, and the global.asax.
I understand the relationship to views, controllers, and actions within controllers. When it comes to creating a view though, does every action associated with a view that is created have to be registered in the global.asax? Example: When creating an empty project, the global.asax already creates a default route for the Home controller with an action of Index and id being optional. So, if I create another view called "AboutUs" based on the Home controller, do I need to registered that in the global.asax as a part of the Home controller?
Is there a one-to-one relationship in terms of controller / action and registration in the global.asax for routes? Can more than one controller action be added to the same statement separated by comma (like as in the using the example Home controller in the global.asax and then adding another action to the same statement or does a new statement need to be added outside of that?
Is there a "best practices" standard to use when creating controllers / views / routes?
I am asking these questions since I am converting a web forms site over to MVC 3 and not finding many good answers to my questions. Looking forward to any and all responses.
Scott Gu had a great post about how MVC routing works on his blog, you should check it out!
There to be a matching pattern in the Global.asax for every route, but not necessarily an explicit match. For your example, if you have a view called 'AboutUs', if you have a controller action named that it will just work due to the pattern matching:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
The row: "{controller}/{action}/{id}" is the pattern that is being matched by the routing engine. It says that any URL path that matches above will be sent to the correct view. For your example if you were to follow the url: Home/AboutUs then it would take you to the Home Controller, About us view, with no parameter.
Regarding your 2nd question, the controller that is selected is also part of the pattern. If you added a second controller, called Contact, and then an action called email, then this url would still follow the same pattern: Contact/Email, so you would not need to add an extra route.
Here are some other great blog posts about how MVC routing works, which will point you in the right direction. One on Asp.Net, a blog post, and on MSDN
No you don't need to register every view you create in Global.asax. The routing will take care of that as long as you follow conventions (i.e routing that is defined for you or one you define yourself should you take this path)
Not again. When you create new Action inside your controller, you typically will need to create a view for that action to render what it intended to do (ajax/json actions can serve as an exception to this rule). But again, you don't need to register anything in Global.asax those. As long as you follow the routing convention defined in that Global file.
I'd say it comes with experience with MVC. In general, every problem has its own solution, so its up to task at hand, rather than general approach. You can, however, start with the default defined for you by MVC 3: I.E. redefined routing and multiple controllers with multiple actions in each. Again, following conventions.
Hope this helps.

How route for generation outgoing URL is chosen in ASP.NET MVC?

Good day!
I'm using ASP.NET MVC 2 and T4MVC and it seems some code magic is happening
When I add this to routes table:
routes.MapRoute(
"Login",
"login/",
MVC.Profile.Login()
);
How does framework know that I want this rule to apply when I write something like this in the view to generate outgoing URL:
<%: Url.Action(MVC.Profile.Login() %>
What if I have multiple different rules (with different params) for the same controller/action pair? Which one will be chosen? Is there anywhere a good description of this behavior?
Thanks in advance!
What I would suggest to help you understand how this work is to separate the magic that T4MVC does from what MVC itself does under the cover.
When you write this with T4MVC:
routes.MapRoute(
"Login",
"login/",
MVC.Profile.Login()
);
It's equivalent to writing this with straight MVC:
routes.MapRoute(
"Login",
"login/",
new { controller = "Profile", action = "Login" }
);
And in the view:
Url.Action(MVC.Profile.Login())
Is the same as
Url.Action("Login", "Profile")
T4MVC gives you the benefit of strong typing/intellisense, but in the end what it does is the same as with straight MVC.
Hopefully this helps clear things up a bit :)
It matches the route patterns in the order you define them. Thats why you have the default pattern as the last one. As soon as it finds a matching pattern, it stops looking.
Edit
Parameters are ignored during route matching. Once a controller method has been selected, mvc uses model binding to assign the parameters to the method variables.
If you can explain what type of url structure you are looking to use, we can probably help you more.
Your example is not valid MVC, you would normally pass the controller name, action and any other parameters, then the routing engine would use all that information to determine which route to use, the more routes you have defined, the more information it will probably require to determine the one YOU want to match

Legacy URL rewriting with query string parameters

I've looked at ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions and several other similar posts for legacy URL routing, but I can't get past the error "The RouteData must contain an item named 'controller' with a non-empty string value." Looking this up on line didn't give me any hints to solve my problem.
I've implemented the Legacy routing class described in the link above, and this is what I've defined in the routing table:
routes.Add(
"Legacy",
new LegacyRoute("fooref.aspx",
"FooRef",
new LegacyRouteHandler())
);
routes.MapRoute(
"FooRef",
"{controller}/{action}",
new
{
controller = "Home",
action = "Index",
foo_id = UrlParameter.Optional,
bar_id = UrlParameter.Optional
}
);
When I use Phil Haack's route debugger, it indicates that fooref.aspx has a match, but when I turn the route debugger off, I get the error above. If I reverse the statement order, I get "Resource not found" for /ctprefer.aspx, which makes sense -- so it appears to be finding that as a valid route when put in the other order.
Where do I need to declare this missing controller reference?
Have routing requirements changed for ASP.NET MVC 2 RTM?
The solution to this is to use an IHttpHandler directly, rather than an MVCHandler. I have posted code that works with MVC 3 to my blog: http://www.olsonsoft.com/blogs/stefanolson/post/Handling-Legacy-Urls-in-AspNet-MVC-3.aspx
...Stefan
You might want to take a look at URL Rewrite module for IIS. It can be used to translate legacy URL to your new MVC URLs without 'polluting' your app with legacy routes.
Don't know if it will fit your solution but it's worth having an alternative.

ASP.NET MVC Routing Questions

I just started using ASP.NET MVC and I have two routing questions.
How do I set up the following routes
in ASP.NET MVC?
domain.com/about-us/
domain.com/contact-us/
domain.com/staff-bios/
I don't want to have a controller specified in the actual url in order to keep the urls shorter. If the urls looked liked this:
domain.com/company/about-us/
domain.com/company/contact-us/
domain.com/company/staff-bios/
it would make more sense to me as I can add a CompanyController and have ActionResults setup for about-us, contact-us, staff-bios and return appropriate views. What am I missing?
What purpose does the name "Default" name have in the default routing rule in Global.asax? Is it used for anything?
Thank you!
I'll answer your second question first - the "Default" is just a name for the route. This can be used if you ever need to refer to a route by name, such as when you want to do URL generation from a route.
Now, for the URLs that you want to set up, you can bypass the controller parameter as long as you're ok with always specifying the same controller as a default. The route might simply look like this:
{action}/{page}
Make sure that it's declared after your other routes, because this will match a lot of URLs that you don't intend to, so you want the other routes to have a crack at it first. Set it up like so:
routes.MapRoute(null, "{action}/{page}",
new { controller = "CompanyController", action = "Company", page = "contact-us" } );
Of course your action method "Company" in your MyDefault controller would need to have a "string page" parameter, but this should do the trick for you. Your Company method would simply check to see if the View existed for whatever the page parameter was, return a 404 if it didn't, or return the View if it did.
Speaking of setting up routes and Phil Haack, I have found his Route Debugger to be invaluable. It's a great tool for when you don't understand why particular routes are being used in place of others or learning how to set up special routing scenarios (such as the one you've mentioned). It's helped clear up many of the intricacies of route creation for me more than any other resource.
To answer your second question about Global.asax, it an optional file used for responding to application level and session-level events raised by ASP.NET or HTTP modules. The Global.asax file resides in the root directory of the ASP.NET application.If you do not define it assumes you have not defined any application handler or session handler. MVC framework uses the routing engine to which the routing rules are defined in the engine, so as to map incoming URL to the correct controller.
From the controller, you can access the ActionName. If there is no specific controller, it will direct to the default page. The default controller is "Home" with its default action "Index". Refer to MSDN:
http://msdn.microsoft.com/en-us/library/2027ewzw%28v=vs.100%29.aspx
Refer to stackoverflow question
What is global.asax used for?
This is a sample of how a default route should look like
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);

Asp.Net MVC routing: best way to have a single element in the URL?

I will take the example of the SO site. To go to the list of questions, the url is www.stackoverflow.com/questions. Behind the scene, this goes to a controller (whose name is unknown) and to one of its actions. Let's say that this is controller=home and action=questions.
How to prevent the user to type www.stackoverflow.com/home/questions which would lead to the same page and would lower the rank of the page as far as SEO is concerned. Does it take a redirect to solve this? Does it take some special routing rules to handle this kind of situation? Something else?
Thanks
I assumed that the controller was questions and the action was index, i.e., the default action as defined by the route handler. Thus there isn't an alternative path to the page.
During Phil Haack's presentation from PDC, Jeff shows some of the source code for Stack Overflow. Among the things he shows is the code for some of the route registrations. He's got these in the controllers, and it's not clear to me that he uses a default route at all. With no default route, you wouldn't need to worry about /home/questions, for example.
As for /questions/index, yes, a permanent redirect is the way to go. You won't get any search engine penalty for a permanent redirect.
Another way to eliminate /home/questions would be to use a route constraint.
You want to use the following route. It is really easy you just create a new route that eliminates the need for the controller to be in the route. You create a template string that just contains the action and you default the controller to the controller you want to use such as "Home".
routes.MapRoute(
"MyRoute",
"{action}",
new { controller = "Home", action = (string)null },
new { action = "[a-zA-z_]+" }
);
Hope this helps.

Resources