Book Image

ASP.NET Core: Cloud-ready, Enterprise Web Application Development

By : Mugilan T. S. Ragupathi, Valerio De Sanctis, James Singleton
Book Image

ASP.NET Core: Cloud-ready, Enterprise Web Application Development

By: Mugilan T. S. Ragupathi, Valerio De Sanctis, James Singleton

Overview of this book

ASP.NET Core is the new, open source, and cross-platform, web-application framework from Microsoft. ASP.NET Core MVC helps you build robust web applications using the Model-View-Controller design. This guide will help you in building applications that can be deployed on non-Windows platforms such as Linux. Starting with an overview of the MVC pattern, you will quickly dive into the aspects that you need to know to get started with ASP.NET. You will learn about the core architecture of model, view, and control. Integrating your application with Bootstrap, validating user input, interacting with databases, and deploying your application are some of the things that you will learn to execute with this fast-paced guide. You will test your knowledge as you build a fully working sample application using the skills you’ve learned throughout the book. Moving forward, this guide will teach you to combine the impressive capabilities of ASP.NET Core and Angular 2. Not only will you learn how Angular 2 can complement your .NET skills and toolkit, you'll also learn everything you need to build a complete, dynamic single-page application. Find out how to get your data model in place and manage an API, before styling and designing your frontend for an exceptional user experience. You will find out how to optimize your application for SEO, identify and secure vulnerabilities, and how to successfully deploy and maintain your application. From here, you will delve into the latest frameworks and software design patterns to improve your application performance. The course offers premium, highly practical content on the recently released ASP.NET Core, and includes material from the following Packt books: Learning ASP.NET Core MVC Programming, ASP.NET Core and Angular 2, and ASP.NET Core 1.0 High Performance.
Table of Contents (5 chapters)

Chapter 7. Routing

Routing is one of the important concepts in the ASP.NET MVC application as it takes care of incoming requests and maps them to the appropriate controller's actions.

In this chapter, we are going to learn about the following topics:

  • Using the MapRoute method to configure routing
  • Different types of routing with examples—convention and attribute-based
  • Using HTTP verbs in attribute-based routing

We briefly discussed routing in Chapter 3 , Controllers. In this chapter, we are going to discuss routing along with several options available to customize it in ASP.NET Core.

Convention-based routing

The routing engine is responsible for mapping the incoming requests to the appropriate action method of the controller.

In the Configure method of the Startup class, we have mapped the following route:

app.UseMvc(routes => 
    { 
        routes.MapRoute(name: "default", 
        template: "{controller=Employee}/{action=Index}/{id?}"); 
    }); 

The MapRoute method has two parameters:

  • name: This represents the name of the route as we could configure multiple routes for the same application.
  • template: This signifies the actual configuration for the route. There are three parts to this configuration value. As we are supplying default parameters, if the values are not passed, it will take the default parameter values.
  • {controller=Employee}: The first value acts as the name of the controller and we use the Employee controller as the default controller when the controller value is not available in the URL.
  • {action=Index}: The Index action method will be acting as the default action method and the second parameter from the URL will be taken as the action method name.
  • {id?}: By specifying "?" after the id parameter, we are saying that id is the optional parameter. If the value is passed as the third parameter, the id value will be taken. Otherwise, it would not be considered.

There is another method with the same functionality. The app.UseMvcWithDefaultRoute() method configures the route "{controller=Employee}/{action=Index}/{id?}". But we have used the earlier method to show that we can customize the route as we want.

Let us see a few examples and observe how our routing engine works. We are assuming the following routing for the preceding examples:

"{controller=Employee}/{action=Index}/{id?}" 

Example 1

URL-http://localhost:49831/

In this URL, we have not passed a value for the controller, action, or id. Since we have not passed anything, it would take the default values for the controller and the action. So, the URL is converted into the following URL by the routing engine:

http://localhost:49831/Employee/Index

Example 2

URL-http://localhost:49831/Employee/

In this URL, we have passed the value for the controller (the first parameter), which is Employee, whereas we did not pass anything for action method (the second parameter) or id (the third parameter). So, the URL will be converted into the following URL by taking the default value for the action method:

http://localhost:49831/Employee/Index

Example 3

URL-http://localhost:49831/Manager/List

The routing engine will take the first parameter, Manager, as the controller method name and the second parameter, List, as the action method name.

Example 4

URL-http://localhost:49831/Manager/Details/2

We have passed all three parameters in this URL. So, the first parameter value, Manager, will be considered as the controller method name, the second parameter value will be considered as the action method name, and the third parameter value will be considered as the id method name.

When defining the map route, we have used the MapRoute method with a couple of parameters. The first parameter, name, represents the name of the route and the second parameter, template, represents the URL pattern to be matched along with the default values:

routes.MapRoute(name: "default",
               template: "{controller=Employee}/{action=Index}/{id?}");

There are other overloaded variations of this MapRoute method. The following is another commonly overloaded MapRoute method, where the incoming URL pattern and the default values are passed for different parameters. The name of the route is FirstRoute and this route will be applied for all URLs starting with Home. The default values for the controller and the action are Home and Index2 respectively:

routes.MapRoute(name:"FirstRoute",
               template:"Home",
               defaults:new {controller ="Home", action="Index2"});

You can define any number of routing maps for your ASP.NET MVC application. There is no restriction or limit on the routing maps. Let us add another routing map to our application. We have added another route map called FirstRoute to our application (highlighted in bold):

public void Configure(IApplicationBuilder app) 
    { 
        app.UseIISPlatformHandler(); 
        app.UseMvc(routes => 
        { 
            routes.MapRoute(name:"FirstRoute",
            template:"Home", defaults:new {controller ="Home", action="Index2"});

            routes.MapRoute(name: "default",
            template: "{controller=Employee}/{action=Index}/{id?}");
        }); 
    } 

And we have added another controller method by the name HomeController with a couple of simple action methods returning different strings:

public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 
 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 

When you try to access the application through the URL, http://localhost:49831/Hello, both routing maps, FirstRoute and the default, match with the URL pattern.

Which map routing, do you think, will get applied in this scenario?

The routing engine maps the incoming URL based on the following factors:

  1. Matching pattern.
  2. On the order defined in the routing engine.

The first factor is an obvious one. For a routing map to be picked up by the routing engine, the pattern of the incoming URL should get matched with the defined template in the routing map.

The second factor is subtle but important. If more than one routing map matches with the incoming URL, the routing engine will pick the first URL as defined in the configuration. For example, if the incoming URL matches with both the FirstRoute and default maps, the routing engine will pick the FirstRoute map as it was defined first in the configuration.

Example 4

If the routing engine could not map the incoming URL to any of the mapping routes, we get an HTTP 404 error, meaning that no resource could be found. You can see the status (200 means OK, 404 means No resource found) by looking at the Network tab in the developer tools as shown in the following screenshot:

Example 4

Attribute-based routing

Until now, we have used convention-based routing. In convention-based routing, we define the routing templates (which are just parameterized strings) in a centralized place these are applicable to all the available controllers. The problem with convention-based routing is that, if we want to define different URL patterns for different controllers, we need to define a custom URL pattern that is common to all the controllers. This makes things difficult.

There is another option for configuring the routing engine-attribute-based routing. In attribute-based routing, instead of configuring all the routing in a centralized location, the configuration will happen at the controller level.

Let us see an example of attribute-based routing.

First, let us remove the convention-based routing that we created earlier in the Configure method in the startup.cs class file:

public void Configure(IApplicationBuilder app) 
    { 
        app.UseIISPlatformHandler(); 
        app.UseMvc(); 
        //app.UseMvc(routes => 
        //{ 
        //    routes.MapRoute(name: "FirstRoute",
        //                    template: "Hello", 
        //                    defaults: new { controller = "Home",  
        //                    action = "Index2" }); 
 
        //    routes.MapRoute(name: "default",
        //                 template:"
        //                {controller=Employee}/{action=Index}/{id?}"); 
        //}); 
    } 

Then, we can configure the routing at the controller itself. In the following code, we have added the routing configuration for the home controller that we created earlier:

namespace Validation.Controllers 
{ 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [Route("Home")] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 
        [Route("Home/Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 
} 

We have used the Route attribute in the action methods of the controller. The value passed in the Route attribute will be acting as the URL pattern. For example, when we access the URL http://localhost:49831/Home/, the Index method of HomeController will be called. When we access the URL http://localhost:49831/Home/Index3, the Index2 method of HomeController will be called. Please note that the URL pattern and action method name do not need to match. In the preceding example, we are calling the Index2 action method but the URL pattern uses Index3, http://localhost:49831/Home/Index3.

When you use attribute-based routing and convention-based routing together, attribute-based routing will take precedence.

Route attribute at the controller level

You will notice that, with the URL pattern for the action methods, Index and Index2, we repeat the controller name, Home, in both URL patterns, Home and Home/Index3. Instead of repeating the controller method name (or any common part in the URL) at the action method level, we can define it at the controller level.

In the following code, the common part of the URL (Home) is defined at the controller level and the unique part is defined at the action method level. When the URL pattern is getting mapped to the action methods of the controller, both route parts (at the controller level and at the action method level) are merged and matched. So there will be no difference between the routes defined earlier and those that follow.

If you want two parameters in attribute-based routing, you can pass them within curly braces. In the following example, we did this for the SayHello action method.

For example, the URL pattern http://localhost:49831/Home/Index3, will still get mapped to Index2 method of the Homecontroller:

namespace Validation.Controllers 
{     
    [Route("Home")] 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [Route("")] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 
 
        [Route("Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
 
 [Route("SayHello/{id}")] 
        public IActionResult SayHello(int id) 
        { 
            return Content("Say Hello action method"+id); 
        } 
    } 
} 

Passing routing values in HTTP action verbs in the Controller

Instead of passing the routing values as Route attributes, we can even pass the routing values in HTTP action verbs such as HTTPGet and HTTPPost.

In the following code, we have used the HTTPGet attribute to pass the route values. For the Index method, we did not pass any value and hence no route value will get appended to the route value defined at the controller method level. For the Index2 method, we are passing the value Index3 and Index3 will get appended to the route value defined at the controller level. Please note that only URLs with GET methods will be mapped to the action methods. If you access the same URL pattern with the POST method, these routes will not get matched and hence these action methods will not get called.

namespace Validation.Controllers 
{     
    [Route("Home")] 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [HttpGet()] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 
 
        [HttpGet("Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 
} 

Route Constraints

Route Constraints enable you to constrain the type of values that you pass to the controller action. For example, if you want to restrict the value to be passed to the int type int, you can do so. The following is one such instance:

[HttpGet("details/{id:int?}")] 
    public IActionResult Details(int id) 
    { 
      return View(); 
    } 

ASP.NET 5 (ASP.NET Core) even supports default parameter values so that you can pass the default parameters:

[HttpGet("details/{id:int = 123}")] 
    public IActionResult Details(int id) 
    { 
      return View(); 
    } 

Summary

In this chapter, we have learned about routing and how it works. We learned about different kinds of routing available. We discussed convention-based routing and attribute-based routing with different examples. We also discussed route constraints and the default parameter values that could be passed.

In the next chapter, we are going to see how we can make the application look good.