ASP.NET MVC Routing Questions - asp.net-mvc

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 }
);

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 do I tell what is part of a route is the controller and what is just a sub folder?

When looking at an MVC page and looking at the form action I can see it is set to:
/admin/WikiAdmin/edit/
So I spent my time looking for a controller called admin. I looked in the routes in the global and nothing was there.
Eventually I found that this url actually maps to the WikiAdmin controller which is confusing. Do does this mean you can have controllers in sub-folders? How does the app know not to forward the request to the admin controller and to actually send it to the WikiAdmin controller?
The admin part of the url is called area. You could read more about areas in this article. And a video here. Basically areas allow you to group multiple controllers sharing some common functionality on the site.
Yes, you can have controllers in sub-folders. With routing it could be a lot of possible URLs.
For example, if you have a route registered as below:
routes.MapRoute(
"admin1",
"admin/{controller}/{action}/",
new { controller = "WikiAdmin", action = "Index"}
);
The url can be /admin/WikiAdmin/Index/ or /admin/WikiAdmin/Edit/ or something else that matches the route. (Assume that there is an Edit action in WikiAdmin controller)
More example, if you have a route registered as below:
routes.MapRoute(
"admin2",
"account/{action}/", //no controller specified in url
new { controller = "WikiAdmin", action = "Index"}
);
Then the url can be /account/Index/ or /account/Edit/ or even /account/. (Because default controller is WikiAdmin and default action is Index)

ASP.NET MVC Default route?

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.

How do you change the controller text in an ASP.NET MVC URL?

I was recently asked to modify a small asp.net mvc application such that the controler name in the urls contained dashes. For example, where I created a controller named ContactUs with a View named Index and Sent the urls would be http://example.com/ContactUs and http://example.com/ContactUs/Sent. The person who asked me to make the change wants the urls to be http://example/contact-us and http://example.com/contact-us/sent.
I don't believe that I can change the name of the controller because a '-' would be an illegal character in a class name.
I was looking for an attribute that I could apply to the controller class that would let me specify the string the controller would use int the url, but I haven't found one yet.
How can I accomplish this?
Simply change the URL used in the route itself to point to the existing controller. In your Global.asax:
routes.MapRoute(
"Contact Us",
"contact-us/{action}/",
new { controller = "ContactUs", action = "Default" }
);
I don't believe you can change the display name of a controller. In the beta, the controller was created using route data "controller" with a "Controller" suffix. This may have changed in RC/RTM, but I'm not sure.
If you create a custom route of "contact-us/{action}" and specify a default value: new { controller = "ContactUs" } you should get the result you are after.
You need to configure routing. In your Global.asax, do the following:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"route-name", "contact-us/{action}", // specify a propriate route name...
new { controller = "ContactUs", action = "Index" }
);
...
As noted by Richard Szalay, the sent action does not need to be specified. If the url misses the .../sent part, it will default to the Index action.
Note that the order of the routes matter when you add routes to the RouteCollection. The first matched route will be selected, and the rest will be ignored.
One of the ASP.NET MVC developers covers what Iconic is talking about. This was something I was looking at today in fact over at haacked. It's worth checking out for custom routes in your MVC architecture.
EDIT: Ah I see, you could use custom routes but that's probably not the best solution in this case. Unless there's a way of dealing with the {controller} before mapping it? If that were possible then you could replace all "-" characters.

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