Prior to ASP.NET MVC Framework, there was a direct relationship between requested URLs and the files on the server hard disk. For example in the Web Forms, a request such as
http://www.site.com/Default.aspx
was matched to a “Default.aspx” file on the server hard disk. On the other hand, in a MVC application, requests are processed by actions in controller classes and apparently, there isn’t a one-to-one correlation to the files on disk. The MVC Framework make use of the “Routing system” to handle URLs requests. This post will give you several examples to teach you how to define your own routes and reach with specific URLs the action methods in your controller classes. Let’s start.
Open Visual Studio 2012 and create a new ASP.NET 4 MVC Web Application named “UrlRouting”, selecting the “Basic” option as template and “Razor” as the view engine. At this point our solution contains a basic structure to start with plus some javascript and jquery support. If you try to run the application you will get an error since there aren’t any controllers and actions yet. So let’s create a few. Right click the “Controllers” folder and select
Add.. -> Controller
Choose the “Empty MVC Controller” option as the template and name the controller “SimpleController”. Change the default Index action contents as follow:
namespace UrlRouting.Controllers { public class SimpleController : Controller { public ActionResult Index() { ViewBag.Controller = "Home"; ViewBag.Action = "Index"; return View("DisplayActionController"); } } }
Create another one controller named “ProductController” in the same way as before and change it’s contents to the following.
namespace UrlRouting.Controllers { public class ProductController : Controller { public ActionResult Index() { ViewBag.Controller = "Product"; ViewBag.Action = "Index"; return View("DisplayActionController"); } public ActionResult ListProducts() { ViewBag.Controller = "Product"; ViewBag.Action = "ListProducts"; return View("DisplayActionController"); } } }
The actions we defined in our controllers are simple actions that pass the specific controller’s and action’s name in a View called “DisplayActionController”, using the ViewBag object. If you aren’t familiar with the ViewBag object you can check this post. Next we will create the above View in order to display details for the action and the controller that processed the requested URL and caused the page to load. Right click the Views/Shared folder and select
Add.. -> View
Name the new View “DisplayActionController”, choose “Razor” as the view engine and leave all other checkboxes unchecked. Change the default contents of the View to the following code.
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>DisplayActionController</title> </head> <body> <div>Controller processed the URL: @ViewBag.Controller</div> <div>Controller action invoked: @ViewBag.Action</div> </body> </html>
Build and run your solution. You should get a “The resource cannot found be found” message again. Add to the default URL the “/Simple/Index” and view the result.
You may wonder what was the change that caused the Index action of the Simple controller to be invoked and where is this actually configured. You define the Routing behavior in your MVC application in a RouteConfig.cs file which relies under the App_Start folder. Go ahead and open this file. Take a look at the “RegisterRoutes” function of that class and it’s contents. Ignore the first line and pay attention at the highlighted code.
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
You define a route for your application using the “MapRoute” method of the “RouteCollection” class. The above code which is what MVC Framework adds by default when you create an application, defines a route named “Default”, with a url pattern:
{controller}/{action}/{id}
This means that the first word after the Root url, will match the “controller” segment, the second will match the “action” segment and the third the “id”. In other words, the respective controller’s action method will be invoked with a parameter id. The last line of the default code
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
defines the default values for the url segments. The default controller is the “HomeController” (which doesn’t exist in our application) and the default action is an “Index” action. The id parameter is defined as optional. But what does “ByDefault” values mean? When you started the application without adding the “Simple/Index” content in the URL, what actually happened is that MVC tried to call the default values for the route properties. So, it tried to call the Index action of a Home controller. That’s why you received that error. Let’s try something else before starting creating our own routes. Request the following URL in your browser (make sure you always change the port number to yours).
http://localhost:49529/Product
Notice that you didn’t specified a value for the “action” segment but only for the controller’s. But as we previously mentioned, by default an “Index” action will be invoked, if nothing is declared for that segment. If you put an action value for that segment such as
http://localhost:49529/Product/Junk
you will get an error since the “Junk” value will match the action segment, resulting the MVC Framework will try to call that action in the ProductController. But since it doesn’t exist an error will occur.
Let’s create our own routes now. Delete the code inside the RegisterRoutes function and paste the following.
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}"); } }
Build and run your application. At the default Root URL you should get an error since we haven’t defined default values either for the controller or the action segment. At this point, you must define values for both the controller and the action you wish to invoke. Let’s create default values now. You supply default values as properties in an anonymous type. Change the “MyRoute” route we create above, to the following.
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { action = "Index" }); }
Build and run the application. You will still get an error at the default root URL, but adding a value /Product or /Simple you will get the DisplayActionController View, since the default value (“Index”) for the action segment was invoked. Add a default value for the controller segment as well.
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Simple", action = "Index" }); }
If you run the application, this time the root URL will match the Index action of the Simple controller.
Notice that you should use the same names as the segments for the anonymous type properties, when defining default values.
Now let’s try something else. Suppose you want to prefix all your URLs with a static word, “Static” for example. You can do that by defining a new route right before the previous one.
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyStaticRoute", "Static/{controller}/{action}", new { controller = "Simple", action = "Index" }); routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Simple", action = "Index" }); }
We declared the new route before the old one, because it’s more specific. If you add it after the previous one, requesting a URL such as
http://localhost:49529/Static/
would result to an error, since the MVC Framework tries to match a URL searching from TOP to BOTTOM in the “RegisterRoutes” function. When it finds a valid route to match the requested URL it stops. In the previous example, it will match the “Static” word to the controller segment of the “MyRoute” route, so it will try to invoke an Index (default) action in a StaticController controller.
One common scenario is to change controllers in your application. Consider that you used to have an “ItemController”, hence your URLs to invoke actions of this controller would look like this:
http://site.com/Item/ControllersAction/
and now you replaced the Item controller with our existing ProductController. You should create a new route to match the old URLs to the new controller like this.
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("ItemToProductRoute", "Item/{action}", new { controller = "Product" }); routes.MapRoute("MyStaticRoute", "Static/{controller}/{action}", new { controller = "Simple", action = "Index" }); routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Simple", action = "Index" }); }
If you need to replace not only the old controller name but an old action too you would have to provide default value for the action adding something like this:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("ItemActionToProductActionRoute", "Item/ReplacedAction", new { controller = "Product", action = "ListProducts" }); //Rest of code omitted }
I hope you have already got an idea about how MVC handles URL requests. To keep the post readable we will stop here for now and we ‘ll continue the MVC URL Routing discussion in next posts, where we are going to see more interesting and advanced features of the MVC’s Routing System.
Categories: ASP.NET
Hello every one, here every one is sharing these
familiarity, thus it’s good to read this weblog, and I used to pay a visit this webpage everyday.