top of page

How to use endpoint routing in ASP.NET Core 3.0 MVC



The Endpoint Routing is the Process by which ASP.NET Core inspects the incoming HTTP requests and maps them to applications executable Endpoint. We define the Endpoint during the application startup. The Routing Module then matches the incoming URL to an Endpoint and dispatches the request to it.


The Endpoint Routing Module also decouples the route matching functions and endpoint dispatching functions using the two separate middleware’s.

  1. The EndPointRoutingMiddleware resolves the HTTP request to an Endpoint (route matching).

  2. The EndpointMiddleware invokes the Endpoint (endpoint dispatching).



The three core concepts involved in Endpoint Routing

There are three overall concepts that you need to understand to be able to understand how endpoint routing works.


These are the following:

  • Endpoint route resolution

  • Endpoint dispatch

  • Endpoint route mapping

Endpoint route resolution

The endpoint route resolution is the concept of looking at the incoming request and mapping the request to an endpoint using route mappings. An endpoint represents the controller action that the incoming request resolves to, along with other metadata attached to the route that matches the request. The job of the route resolution middleware is to construct and Endpoint object using the route information from the route that it resolves based on the route mappings. The middleware then places that object into the http context where other middleware the come after the endpoint routing middleware in the pipeline can access the endpoint object and use the route information within.


Prior to endpoint routing, route resolution was done in the MVC middleware at the end of the middleware pipeline. The current 2.2 version of the framework adds a new endpoint route resolution middleware that can be placed at any point in the pipeline, but keeps the endpoint dispatch in the MVC middleware. This will change in the 3.0 version where the endpoint dispatch will happen in a separate endpoint dispatch middleware that will replace the MVC middleware.


Endpoint dispatch

Endpoint dispatch is the process of invoking the controller action method that corresponds to the endpoint that was resolved by the endpoint routing middleware. The endpoint dispatch middleware is the last middleware in the pipeline that grabs the endpoint object from the http context and dispatches to particular controller action that the resolved endpoint specifies. Currently in version 2.2 dispatching to the action method is done in the MVC middleware at the end of the pipeline.


In the version 3.0 preview 3, the MVC middleware is removed. Instead the endpoint dispatch happens at the end of the middleware pipeline by default. Because the MVC middleware is removed, the route map configuration that is usually passed to the MVC middleware, is instead passed to the endpoint route resolution middleware.


Based on the current source code, the upcoming 3.0 final release should have a new endpoint routing middleware that is placed at the end of the pipeline to make the endpoint dispatch explicit again. The route map configuration will be passed to this new middleware instead of the endpoint route resolution middleware as it is in version 3 preview 3.


Endpoint route mapping

When we define route middleware we can optionally pass in a lambda function that contains route mappings that override the default route mapping that ASP.NET Core MVC middleware extension method specifies. Route mappings are used by the route resolution process to match the incoming request parameters to a route specified in the rout map. With the new endpoint routing feature the ASP.NET Core team had to decide which middleware, the endpoint resolution or the endpoint dispatch middleware, should get the route mapping configuration lambda as a parameter.



How Endpoint Routing Works

The Endpoint Routing has three components

  1. Defining the Endpoints.

  2. Route matching & Constructing an Endpoint (UseRouting).

  3. Endpoint Execution (UseEndpoints).

To understand how it works, Create a new ASP.NET Core application in VS 2019 ( Min Version 16.9.1 ) and .NET 5.0. Choose the Web Application (Model-View-Controller) Template.


Open the startup class.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
 
    app.UseRouting();
 
    app.UseAuthorization();
 
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
} 

Configure the Endpoint

We configure the Endpoint in the app.UseEndpoints method.

The following method adds the default MVC Conventional route using the MapControllerRoute method. The following adds the default conventional route to MVC Controllers using a pattern.

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

To setup an attribute based Routing use the method MapControllers. We use the attribute based Routing to create a route to Rest API (Web API Controller Action Method). You can also use it to create a route to MVC Controller action method.

endpoints.MapControllers(); 

MapControllerRoute & MapControllers methods hides all the complexities of setting up the Endpoint from us. Both sets up the Endpoint to Controller action methods


You can also create an Endpoint to a custom delegate using MapGet method. MapGet accepts two argument. One is Route Pattern (/ in the example) and a request delegate.

endpoints.MapGet("/", async context =>
   {
      await context.Response.WriteAsync("Hello World");
   });

UseRouting

The EndPointRoutingMiddleware resolves the incoming HTTP requests and constructs an Endpoint.

We register it very early in the middleware pipeline using the UseRouting method.

Its purpose is to

  1. Parse the incoming URL

  2. Resolve the URL and construct the Endpoint.

  3. Updates the HTTP Context object with the endpoint using the SetEndpoint method.

The Endpoint objects are immutable and cannot be modified after creation.


The Middleware running after UseRouting can access the endpoint from the HTTP Context and take action. For example, an authorization middleware can query the metadata of the endpoint and apply the correct authorization policy based on that information.

//All Middleware till here cannot access the Endpoint
 
// Add the EndpointRoutingMiddleware
app.UseRouting();
 
// All middleware from here onwards can access the Endpoint from the HTTP Context 


UseEndpoints

The EndpointMiddleware is responsible for execution the Endpoint. We register the it using the UseEndpoints method. It reads the Endpoint, which was selected by the Route Matching middleware and runs the delegate associated with it.


Remember, we also configure the endpoints in the UseEndpoints method

    app.UseEndpoints(endpoints =>
    {
        // Define endpoint here
    });
  1. The EndpointMiddleware middleware is terminal Middleware when a match is found

  2. The middleware after UseEndpoints execute only when no match is found.


Endpoint Routing in Action

In the following example, we are using the MapGet to create two custom Endpoints

MapGet is an extension method, on EndpointRouteBuilder and accepts two arguments.


The first argument is route pattern as string. The second argument is a request delegate, which will be executed when the Endpoint is matched.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
 
    //All Middleware till here cannot access the Endpoint
 
    app.UseRouting();
 
 
    //All middleware from here onwards can access the Endpoint from the HTTP Context 
 
    app.UseEndpoints(endpoints =>
    {
 
 
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World");
        });
 
        endpoints.MapGet("/hello", async context =>
        {
            await context.Response.WriteAsync(helloWorld());
        });
 
 
    });
}
 
private string helloWorld()
{
    return "Hello World from a Method";
}

The Code above defines two Endpoints. One is root URL / and the other one is /hello.

Request delegates in both the Endpoints print Hello World to the response stream. The second delegate uses the the method helloWorld() to do so.


When the app starts, UseRouting registers the Route Matching Middleware EndPointRoutingMiddleware. UseEndpoints method registers the Endpoint execution middleware EndpointMiddleware. UseEndpoints also configures the Endpoints.


When the request arrives to root URL / or to the /hello URL, the Route Matching Middleware will construct the Endpoint and updates the context. The later in the middleware the EndpointMiddleware reads the Endpoint from the context and executes its delegate.


Any request other than the above, will not have any Endpoints, and hence will throw the 404 error. Also note that MapGet maps to HTTP GET requests only. Any other request like POST, PUT etc returns 404 status code.


Reading Endpoints in Middleware

Any Middleware between the UseRouting and UseEndpoints can access the the Endpoint from the context using the GetEndpoint mehod.


In the following code adds a custom middleware after UseRouting. If Endpoint is not null, it extracts the DisplayName, RoutePattern & metdata from the Endpoint and writes it to the response stream.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
 
    //All Middleware till here cannot access the Endpoint
 
    app.UseRouting();
 
    app.Use(async (context, next) =>
    {
        var endpoint = context.GetEndpoint();
 
        if (endpoint != null)
        {
            await context.Response.WriteAsync("<html> Endpoint :" + endpoint.DisplayName + " <br>");
 
            if (endpoint is RouteEndpoint routeEndpoint)
            {
 
                await context.Response.WriteAsync("RoutePattern :" + routeEndpoint.RoutePattern.RawText + " <br>");
 
            }
 
            foreach (var metadata in endpoint.Metadata)
            {
                await context.Response.WriteAsync("metadata : " + metadata + " <br>");
 
            }
 
        }
        else
        {
            await context.Response.WriteAsync("End point is null");
        }
 
        await context.Response.WriteAsync("</html>");
        await next();
    });
 
 
 
    app.UseEndpoints(endpoints =>
    {
 
 
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World");
        });
 
        endpoints.MapGet("/hello", async context =>
        {
            await context.Response.WriteAsync(helloWorld());
        });
 
    });
}

Set a breakpoint to inspect the properties of the Endpoint.


Defining the Endpoints

As shown earlier, we configure the Endpoint in the app.UseEndpoints method.

The ASP.NET Core supports many scenarios in developing apps. They include web apps like MVC, Razor Pages & Blazor. You can also build Web APIs or real time Apps using SignalR. Use gRPC to build Remote Procedure call apps, etc.


The ASP.NET Core provides several extension methods to setup & configure the Endpoints.

You can create endpoint to custom delegates using the map methods like MapGet, MapPost, MapPut, MapDelete, & Map


Use MapRazorPages to adds all the required Endpoints which maps to Razor Pages

endpoints.MapRazorPages(); 

Similarly, you can use the MapHub to configure the SignalR endpoint

endpoints.MapHub<ChatHub>("/chathub"); 

Use MapGrpcService to Create Endpoint to a gRPC service.

endpoints.MapGrpcService<SomeGrpcService>(); 

Routing to Controller Action

MVC & Web API require us to setup Endpoint to each controller action methods. There are two different ways by which we can set up routes.

  1. Convention-based routing

  2. Attribute routing

Convention-based routing

Conventional based routing is typically used with the controllers and views. It creates routes based on a series of conventions. We define it using the endpoints.MapControllerRoute method.

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

In the above example, the MapControllerRoute creates a single route, which is named as default and the URL Pattern of the route is {controller=Home}/{action=Index}/{id?}



Attribute routing

The attribute routing uses the attributes defined directly on the controller action to define the routes. Attribute routing gives you more control over the URLs in your web application.


To make use of the attribute routing use the MapControllers method.

endpoints.MapControllers();


Resource: Tektutorialshub, Microsoft


The Tech Platform

0 comments
bottom of page