Views / Controller Actions / Routes / Gobal.asax - asp.net-mvc

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.

Related

ASP.Net MVC with complex routes - how to keep it "sane"?

I have a client who wishes to use a URL naming convention along the lines of:
/{subjectarea}/{subject}/{action}
Which is fine - this works brilliantly, with one controller per subject area, and having the action after the id (subject) is no issue at all.
However, it then gets complicated, as the client then wants to further continue the hierarchy:
/{subjectarea}/{subject}/{action}/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
I have a controller for the tightly related subject (its just another subject area) which handles all of the admin side, but the client insists on having the public view hung off of the parent subject rather than its own root.
How can I do this while avoiding breaking the entire principals of MVC, and also avoiding re-implementing a ton of ASP.Net MVC provided functionality in my subject area controller just to be able to handle the related subjects from that same controller?
Is it possible to somehow call the related subjects controller from within the parent subject controller, and return the resulting view (as this would keep the separation of functionality for the subjects to their own controllers)? If that is possible, it would solve a heck of a lot of issues with this.
Here is the solution which solves my given issue - hope it solves someone elses.
As mentioned in my comment to Robert Harvey, all I actually need is another route which doesn't use the first two or three components as the controller, action and id, but instead takes those values from later on - if you hang this off of a static value in the route as well, its much easier to do.
So, here is the url I decided on to simplify the route:
/{subjectarea}/{subject}/related/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
The route which satisfies this URL is as follows:
routes.MapRoute(
"RelatedSubjects",
"{parentcontroller}/{parentsubject}/related/{controller}/{id}/{action}",
new { controller = "shoes", action = "view", id = "all" }
);
On the subsequent controller action, I can ask for parameter values for parentcontroller and parentsubject so I can filter out the related item to just be specific to the given parent subject - problem solved!
This route needs to be above the ones which just deal with the first two values, otherwise you run the risk of another route map hijacking the request.
I could do this entirely without the /related/ static portion as the route could easily match on number of values alone, and infact I may indeed do so - however, I consider it better for later administration if there is a static item in there to confirm the use of the route.
I hope this helps someone!
One way you can do it is specify a wildcard route (notice the asterisk):
routes.MapRoute("subjects", "{action}/{*path}",
new { controller = "Subjects", action = "Index" });
This allows the controller to receive the entire path string after the action.
You can then obtain the hierarchy of subjects in the controller method like so:
string[] subjects = path.Split('/');
Once you have that, you can do anything you want, including dispatching different subjects to different handling methods for processing.

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 basic routing with no View name

This is a basic question about the routing machinery. From a new MVC project, I have this in the HomeController:
public ActionResult MyPage()
{
return View();
}
In the Views\Home folder, I have MyPage.aspx. The routing is still the default of {controller}/{action}/{id}. When I type in http://localhost:1790/Home/MyPage, it is correclty routes to MyPage.aspx. Since I haven't said anything about which view to render, how does ASP.NET MVC know to correctly route to MyPage.aspx? Looks as though the ActionResult name can also be used as the View/aspx page name...unless there is something I misunderstand in how the routing works. I can see how I end up in the Home folder, since the controller name corresponds to the View sub folder name. But does the Action name also correspond to the aspx name?
Would that work if the page was a PHP?
ASP.NET MVC subscribes to what is known as the Convention over Configuration paradigm whereas if you follow their conventions, basic things such as routing concerns will happen for you. But they also allow you to configure them if desired.
MVC implicitly assumes that if you return just View(), that you want View("MyPage") (i.e. the action name). No sense in repeating yourself unnecessarily.
It won't find a PHP file by default, but I'm sure you could override that behavior if you really wanted to. I can't imagine a sane scenario where you would be mixing PHP and ASP.NET MVC, but who knows :)
Action name is the same as the view / partial view name.
asp.net mvc doesn't work with php as far as I'm aware.
As has already been stated, ASP.NET MVC uses convention over configuration. Out of the box, your folder structure is something like this (only showing relevant portions and doing it from memory so...)
Site Root
+ Controllers
HomeController.cs
AccountController.cs
+ Views
+ Home
Index.aspx
+ Account
Index.aspx
+ Shared
The default routing handler is something similar to the following:
"{controller}/{action}/{id}"
There are default values for the route, but if you have a url that is a/b/c, it will look for action a on controller aController and pass it c as a parameter if said method on the controller accepts parameters.
A couple of things about that then need to be clarified. Again, convention over configuration:
1) All controller classes must end with Controller if you're using the default engine. That way, when a request comes in and the {controller} value is parsed, the engine adds Controller to it, looks in the Controller folder (and, thus, namespace), and locates the class.
2) By default -- this can be changed -- all views for a controller must reside in the Views/{controller} folder or in the Views/Shared folder.
3) Public methods on a controller are, by default, actions. You can hide this with an attribute to make themethod unavailable to the engine, but by default they are public.
So, when a request comes in the route is compared against all known routes (global.asax) and the first route that matches the request is accepted. The route is then parsed into the component parts to determine the controller, action, and parameters for the action.
Once the controller is identified, the engine instantiates an instance of that controller and executes the matching method (action) should it be found.
The action will return an ActionResult. View is an extensino method that actually returns ViewResult (if I remember that correctly). The default view for an action is a view of the same name as teh action residing in the Views/{ControllerName} folder.
Routing is a beast unto itself and I'd recommend a good bit of reading on it if you're going to play with it. Minutes to understand but a lifetime to master sorta thing.
To my knowledge, BTW, there is no engine that will use a php page as a view for a controller action.

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