Routing in ASP.NET
Tags: aspnet, webdev, csharp, programming
Published: 2021-01-11 19:10:14

This is the third post in the series of posts on building an ASP.NET web application. In this post, you will learn how routing works in an ASP.NET application.

After reading this post, you will understand:

1.  the need for routing and how it works in ASP.NET
2.  how the conventional and attribute routing works and the use of each
3.  how to change the generated code to send HTML view custom to your application.

What is routing?
================

Routing is responsible for mapping an HTTP request to a controller action, which is an executable endpoint. An action method executes in response to an appropriate request. It does the work to process the request using the business logic in the models, and passes the data to the view. A view renders the data in a suitable format.

An important benefit of using routing to handle the requests is that it decouples the code from the URL that is used to execute it. This allows you to change the URL by making changes in the routing system, instead of modifying your code. You can create different routing patterns to map different URLs to different action methods on the controller.

For example, a request for *http://localhost:5001/posts* will be handled by an action method on the *PostsController*. This method invokes the business logic by fetching *Post.cs* model from the database and sending it to the view.

![](https://akshaykhot.s3.amazonaws.com/router.png)

In ASP.NET, you can either use conventional routing, or set up your custom routes using attribute-based routing. In real world, we use mixed-routing to use the conventions most of the time and using custom routes when necessary.

Conventional Routing
====================

When you created your app using the generator, the default routes were created in the *Startup.cs* file. In the *Configure()* method in *Startup.cs*, you will find following code that creates the default convention-based routing with the given name and pattern.

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });

It is called conventional routing because the router directly infers the controller and action method from the URL itself, by following a convention on the URL segments. A segment is a part of a URL that's separated from other segments by '/' character.

The first segment of the URL maps to the name of the selected controller. The second segment maps to the name of the action on that controller. The third segment {id?} is used for an optional id, which is a parameter id on the action method. The default values for the controller and the action are HomeController and Index().

For example, given a URL /Products/Details/5, the router will select the *Details (int id)* action method on the *ProductsController*, passing 5 as the id. URL */posts* will be mapped to the *Index()* method on the *PostsController*.\
The framework also provides a convenience method, named [MapDefaultControllerRoute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.controllerendpointroutebuilderextensions.mapdefaultcontrollerroute?view=aspnetcore-5.0), which you can use to replace the MapControllerRoute() method. For example, the following code sets up the convention-based routing following the same pattern.

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });

Conventional routing makes your code predictable, and the URLs readable and meaningful to both the user and the search engines. It is often a useful starting point for CRUD-based web applications. It also enforces consistency across your controllers.

Attribute Routing
=================

Now, the conventional routing works as long as your application code conforms to the conventions. They are also not enough if you want to create a web api for your app and want to name your action methods differently than the URL paths.

For example, having a descriptive method name *GetAllProductsWithCategory(string category)* on *ConsumerProductsController* will force your URL to be excessively long, i.e. *ConsumerProductsController/GetAllProductsWithCategory.*

In attribute routing, you can use attributes to connect a given URL path to an action method. The *[Route]* attributes can be placed on the action methods themselves, or the controller. The controller and action names don't have any control over which action is matched.

For example, the */Products/Category* URL will match the *GetAllProductsWithCategory()* action method in the *HomeController*, and you are not forced to use the longer method name.

    public class ConsumerProductsController : Controller
    {
        [Route("Products/Category/{cat}")]
        public IActionResult GetAllProductsWithCategory(string cat)
        {
            ..
        }
    }

To learn more about routing in ASP.NET, see the official Microsoft [documentation](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-5.0).

Hello, World
=============

Now that we have a basic understanding of routing and how it works in an ASP.NET application, the next step is to modify the generated code to send custom HTML. We will keep things simple, and send a simple "Hello, World" string as a response, when the user navigates to the /blog/welcome page.

To get your app saying "hello world", you will need three things. A route, a controller with an action, and a view. As we saw earlier, a route maps a request to a controller action. A controller action performs the necessary work to handle and process the request, and prepares any data for the view. A view then displays the data to the user.

We will stick to the conventional routing to keep things simple and easy to understand. To map the route *http://localhost:5001/blog*, we will need to create a *BlogController* in the Controllers directory, with an *Index()* action method.

    // Controllers/BlogController.cs

    using Microsoft.AspNetCore.Mvc;

    public class BlogController : Controller
    {
        // Maps to /blog
        public IActionResult Index()
        {
            return View();
        }
    }

The *Index* action returns a View by calling the helper method *View()*, which is defined in the *Controller* base class.

By following convention, when an action does not explicitly render a view with a name, i.e. *View("Welcome")*, ASP.NET will automatically render a view that matches the name of the controller and action. Views are located in the *Views/* directory, so the *Index* action will render *Views/Blog/Index.cshtml* by default.

Now that we know which view will be rendered by default, let's create the view file *Views/Blog/Index.cshtml* with following content.

<h1>Hello, World</h1>

Now start your application by running the command *'dotnet run'*, and navigate to the *https://localhost:5001/blog/* URL (assuming your app is running on the port 5001).

You will be greeted with the glorious words that every programmer in the history of programming has witnessed on running their first successful program.

![](https://akshaykhot.s3.amazonaws.com/05-01-2021T:20:00:24_image.png)

So far, we've discussed routes, controllers, actions, and views, which form the fundamental pieces of an MVC web application. Now that we have a controller and a view to work with, we will have a look at models. We will create a new model class, and display the model to the user using the view.

Create a Model
===============

A model is a plain C# class with properties that are used to represent real-world data. A model class can also contain the business logic specific to the application.

Let's create a new class in the Models/ directory called *Post.cs.* The name of the class is singular because an instance of Post will represent a single blog post in our weblog.

    namespace weblog.Models
    {
        public class Post
        {
            public int ID { get; set; }

            public string Title { get; set; }

            public string Body { get; set; }
        }
    }

Render the Model
================

Now that we have a model, it's time to put it to use. We will instantiate a new post in the controller and displaying it in the browser using the view.

In the BlogController, add a new action method Post, as follows.

    using weblog.Models;
    using Microsoft.AspNetCore.Mvc;

    public class BlogController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Post()
        {
            var post = new Post
            {
                ID = 1,
                Title = "Hello World",
                Body = "This is my first blog post"
            };

            // Pass the post model to the view
            return View(post);
        }
    }

As we are following the conventional routing, the framework will try to find the Post.cshtml under the Blog directory, to render our model. Let's create this view, which is nothing but an HTML template with embedded [Razor markup](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0). Razor markup is code that interacts with HTML markup to produce a webpage that's sent to the client.

    @model Post
    @{
        Post post = Model;
    }

    <h4>@(post.ID). @(post.Title)</h4>
    <div>
        @post.Body
    </div>

There are a few things of importance in the above code:

1.  The model that we passed from the controller, is received by the View in the Model variable.
2.  On the first line, we tell the Razor view that the type of the Model is a Post, so it can help us with the intellisense.
3.  To use C# code in the HTML, use an @ character, followed by the code.

That's all. Now that we have our model and view ready, the final step is to restart our app, and navigate to the url *https://localhost:5001/blog/post.* You will be greeted with the first post that we just created.

![](https://akshaykhot.s3.amazonaws.com/05-01-2021T:20:01:10_image.png)

In the next post, we will work with the entity framework, to create a model backed by the database.