Middleware in Dotnet Core

Introduction


Middleware is very important to understand if you are working in ASP.NET CORE Web Application because it impacts our request pipeline and helps us to achieve customized features like authenticating the request before it hits the action method, handling exceptions, routing, etc.…  The role of Middleware is similar to HttpModules and HttpHandlers in Legacy .Net Framework.


Normally when we create a new ASP.Net core project then some default middlewares are used in Configure method which is required to run the application like Routing, Development Exception Page, and Authorization middlewares.

Let’s see the default middleware which is used when we create an ASP.Net Core Web API project using .Net Core Version 3.1

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>

    {

        endpoints.MapControllers();

    });

}


RequestDelegate

Middlewares in ASP.NET Core handles the request pipeline with the help of RequestDelegate and it’s used in the Run, Use and Map method. RequestDelegate is a delegate that accepts HttpContext as a parameter and returns a Task and below is the signature of the same.

RequestDelegate Signature


public delegate Task RequestDelegate(HttpContext context);


Understanding Run method


public static void Run(this IApplicationBuilder app, RequestDelegate handler)


Run is an extension method of IApplicationBuilder that accepts a delegate parameter of the type RequestDelegate.

Let’s check a simple middleware using the Run method which returns “Hello World” in the response.

public void Configure(IApplicationBuilder app1, IWebHostEnvironment env)

{

    app1.Run(async (context1) =>

    {

        await context1.Response.WriteAsync("Hello World!");

    });

}


In the above example, you can see we have added a middleware using the Run method which returns “Hello World!” in the response. Run is the terminal middleware delegate to the request pipeline which means we cannot call another middleware after using the Run method.

Understanding Use method

Use is another extension method of IApplicationBuilder in which we can call another middleware with the help of the next function. If we don’t use call the next parameter then the request will short-circuited and will not call the next middleware mentioned in the pipeline.

app.Use(async (context, next) =>

{

    await context.Response.WriteAsync("Before Invoking from First app.Use()\n");

    await next();

    await context.Response.WriteAsync("After Invoking from First app.Use()\n");

});

app.Use(async (context, next) =>

{

    await context.Response.WriteAsync("Before Invoke from Second app.Use()\n");

});

app.Run(async (context) =>

{

    await context.Response.WriteAsync("Hello World!");

});


run_extension_method_dotnet_core.png


As we can see from the response after calling the second app.Use it doesn’t call the app.Run because of the second app.Use doesn’t call the next parameter to call the next middleware instead it short circuits the request pipeline.

Signature of Use Extension method


public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)


The middleware parameter handles the request and calls the next function.

Understanding Map method

The map is another extension method of IApplicationBuilder where middleware is called based on the matching of the route path.

app.Map("/route1", appMap =>

{

    appMap.Run(async context =>

    {

        await context.Response.WriteAsync("Hello World from Route1");

    });

});

app.Map("/route2", appMap => {

    appMap.Run(async context =>

    {

        await context.Response.WriteAsync("Hello World from Route2");

    });

});


map_extension_method_route1.png

map_extension_method_route2.png


As we can see above when we are hitting the route “/route1” it’s calling only the middleware mentioned with “/route1” and likewise it’s doing the same for “/route2” where it’s calling the middleware mentioned within “/route2”.

The map has various overloads available where it calls middleware based on the request starts with the given path.

Signature of Map Extension method


public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)


Ordering in Middleware


Ordering in Middleware is important as the request will hit the middleware first which is mentioned first in Configure method. For example, ExceptionHandler middleware should always be mentioned first so that exceptions can be handled if any exceptions occurred in the preceding middleware. The following diagram mentioned in Microsoft Docs Link shows the complete request pipeline for the ASP.NET Core MVC app

middleware_order.png


Static Files Middleware is mentioned before Routing middleware because any request which wants to access static files can be served directly without being called Routing middleware.


Sample Middleware to Authenticate Request


Let’s say we want to check all the incoming requests for the “token” value in the request header therefore we will allow the request to hit the Controller endpoint only if the request contains a header named “token”. We will create a middleware to achieve the same.


public class ValidateRequestMiddleware

{

    private readonly RequestDelegate _next;

    public ValidateRequestMiddleware(RequestDelegate next)

    {

        _next = next ?? throw new ArgumentNullException(nameof(next));

    }

    public Task Invoke(HttpContext httpContext)

    {

        if (!httpContext.Request.Headers.ContainsKey("token"))

        {

            httpContext.Response.StatusCode = 401;

            return httpContext.Response.WriteAsync("UnAuthorized");

        }

        else

        {

            return _next(httpContext);

        }

    }

}


Now we can configure this middleware in Configure method as below

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

    if (env.IsDevelopment())

    {

        app.UseDeveloperExceptionPage();

    }

    app.UseRouting();

    app.UseAuthorization();

    app.UseMiddleware<ValidateRequestMiddleware>();

    app.UseEndpoints(endpoints =>

    {

        endpoints.MapControllers();

    });

}


Note the Order in which we mentioned the middleware is very important and if we call app.UseMiddleware<ValidateRequestMiddleware>(); after app.UseEndpoints then Controller endpoint can be accessed irrespective of token value is present in the request header.

With Token


http_request_with_token_middlware_authenticate.png

Without Token


http_request_without_token_middlware_authenticate.png


Built-in Middlewares


ASP.NET Core comes with various middleware which can be used for various purposes. If we are creating any project type like Web API, MVC or Razor, etc. using Microsoft Visual Studio then we can see some built-in middlewares are mentioned in Configure method based on project type. Some of the most common middlewares are Routing, Authorization, Exception Handler, etc...


Middleware

Description

DeveloperExceptionPage

Generates a page with error information used in the DEV environment.

CORS

Configure Cross-Origin Resource Sharing

Authentication

Configure Authentication

Authorization

Configure Authorization

Static Files

Helps serve static files and Directory browsing

MVC

Process request with MVC or Razor Pages

EndpointRouting

Define and constrains request routes.


There are much more middleware and the above table are some of the widely used middleware.

Conclusion

Middleware is important in ASP.NET Core as it helps us define how the application will respond to the HTTP request. It helps us to check exception details, route the requests to endpoints, log, helps to short circuit the request based on the condition. Order of the middleware is important for any application because it executes based on the order it's mentioned in the Configure method.

Share This Post

Linkedin
Fb Share
Twitter Share
Reddit Share

Support Me

Buy Me A Coffee