Understanding middleware in ASP.NET Core

Ahead of the course “C # ASP.NET Core developer” we invite you to sign up for an open lesson on the topic “Logging and tracing requests in asp.net core”

In the meantime, we are sharing with you a traditional useful translation.


This article covers the concepts of Middleware in ASP.NET Core. By the end of this article, you’ll have a clear understanding of the following points:

  • What is Middleware?

  • Why does the order of Middleware matter?

  • Run, Use and Map methods.

  • How to create your own Middleware?

  • How to implement directory browsing using Middleware?

What is Middleware?

Middleware (middleware or middleware) is a piece of code in an application’s pipeline used to process requests and responses.

For example, we can have a middleware component for user authentication, a middleware component for error handling, and another middleware component for serving static files such as JavaScript files, CSS files, various kinds of images, etc.

Middleware can be embedded as part of the .NET Core framework, added via NuGet packages, or written by the user. Middleware components are configured in the Configure method of the application launch class (Startup). The Configure method builds a request processing pipeline in an ASP.NET Core application. It consists of a series of request delegates called one after the other.

The figure below shows how the request is handled by middleware components.

Typically, each middleware handles incoming requests and passes execution to the next middleware for further processing.

But the middleware component may also decide not to call the next piece of middleware in the pipeline. This is called short-circuiting, or request pipeline termination. Closing is often desirable because it avoids unnecessary work. For example, if the request is for a static file, such as a CSS file, JavaScript, image, etc., the middleware for the static files can process and serve that request, and then close the rest of the pipeline.

Let’s create an ASP.NET Core web application and look at the default middleware configuration in the Configure method of the Startup class.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    
{    
    if (env.IsDevelopment())    
    {    
        //This middleware is used reports app runtime errors in development environment.  
        app.UseDeveloperExceptionPage();    
    }    
    else    
    {    
        //This middleware is catches exceptions thrown in production environment.   
        app.UseExceptionHandler("/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(); //adds the Strict-Transport-Security header.    
    }    
    //This middleware is used to redirects HTTP requests to HTTPS.  
    app.UseHttpsRedirection();   
    
    //This middleware is used to returns static files and short-circuits further request processing.   
    app.UseStaticFiles();  
    
    //This middleware is used to route requests.   
    app.UseRouting();   
    
    //This middleware is used to authorizes a user to access secure resources.  
    app.UseAuthorization();    
    
    //This middleware is used to add Razor Pages endpoints to the request pipeline.    
    app.UseEndpoints(endpoints =>    
    {    
        endpoints.MapRazorPages();               
    });    
} 

The ASP.NET Core framework provides built-in middleware components that we can easily use by adding to the Configure method. Check out Microsoft documentation for more details.

Streamlining Middleware

Middleware components are executed in the order in which they are added to the pipeline, so be careful and add middleware in the correct order, otherwise the application may not work as you expect. The ordering of middleware is important for security, performance, and functionality.

The following middleware components are intended for common application scenarios and are arranged in the recommended order:

The first middleware component in the configuration received the request, changed it (if necessary), and passed control to the next middleware. Likewise, the first middleware component is executed last when processing the response if we echo back. This is why exception handling delegates must be called very early in the pipeline so that they can inspect the result and display the possible exception in a browser and client friendly way.

Run, Use and Map Methods

app.Run ()

This method adds a middleware component as Run[Middleware]which will be executed at the end of the pipeline. Typically, it acts as a closing middleware and is added at the end of the request pipeline because it cannot call the next middleware component.

app.Use ()

This method is used to configure multiple middleware. Unlike app.Run (), we can include a next parameter that invokes the next request delegate in the pipeline. We can also close (complete) the pipeline without calling the next parameter.

Let’s see the following example with app.Use() and app.Run() and analyze the result / response:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    
{    
    app.Use(async (context, next) =>    
    {    
        await context.Response.WriteAsync("Before Invoke from 1st app.Use()n");    
        await next();    
        await context.Response.WriteAsync("After Invoke from 1st app.Use()n");    
    });    
    
    app.Use(async (context, next) =>    
    {    
        await context.Response.WriteAsync("Before Invoke from 2nd app.Use()n");    
        await next();    
        await context.Response.WriteAsync("After Invoke from 2nd app.Use()n");    
    });    
    
    app.Run(async (context) =>    
    {    
        await context.Response.WriteAsync("Hello from 1st app.Run()n");    
    });    
    
    // the following will never be executed    
    app.Run(async (context) =>    
    {    
        await context.Response.WriteAsync("Hello from 2nd app.Run()n");    
    });    
}    

First delegate app.Run() ends the conveyor. This example will run only the first delegate («Hello from 1st app.Run()») and the request will never reach the second method Run

app.Map ()

This extension method is used as a shorthand for pipeline branching. Map forks the request pipeline based on the request path. If the request path starts with the specified path, the branch is executed.

Let’s see the following example with app.Map() and analyze the result / response:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
{  
    app.Map("/m1", HandleMapOne);  
    app.Map("/m2", appMap => {  
        appMap.Run(async context =>  
        {  
            await context.Response.WriteAsync("Hello from 2nd app.Map()");  
        });  
    });  
    app.Run(async (context) =>  
    {  
        await context.Response.WriteAsync("Hello from app.Run()");  
    });  
}  
private static void HandleMapOne(IApplicationBuilder app)  
{  
    app.Run(async context =>  
    {  
        await context.Response.WriteAsync("Hello from 1st app.Map()");  
    });   
}  

The following table shows requests and responses from localhost using the above code.

Request

Response

https: // localhost: 44362 /

Hello from app.Run ()

https: // localhost: 44362 / m1

Hello from 1st app.Map ()

https: // localhost: 44362 / m1 / xyz

Hello from 1st app.Map ()

https: // localhost: 44362 / m2

Hello from 2nd app.Map ()

https: // localhost: 44362 / m500

Hello from app.Run ()

Creating your own Middleware

Middleware usually encapsulated in a class and exposed using an extension method. Middleware can be created using a class with a method InvokeAsync() and the type parameter RequestDelegate in the constructor. A type RequestDelegate required to execute the next middleware in the sequence.

Consider an example where we need to create our own middleware to register the request url in the web application.

public class LogURLMiddleware  
{  
    private readonly RequestDelegate _next;  
    private readonly ILogger<LogURLMiddleware> _logger;  
    public LogURLMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)  
    {  
        _next = next;  
        _logger = loggerFactory?.CreateLogger<LogURLMiddleware>() ??  
        throw new ArgumentNullException(nameof(loggerFactory));  
    }  
    public async Task InvokeAsync(HttpContext context)  
    {  
        _logger.LogInformation($"Request URL: {Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(context.Request)}");  
        await this._next(context);  
    }
}
public static class LogURLMiddlewareExtensions  
{  
    public static IApplicationBuilder UseLogUrl(this IApplicationBuilder app)  
    {  
        return app.UseMiddleware<LogURLMiddleware>();  
    }  
} 

In the Configure method:

app.UseLogUrl(); 

Implementing Directory Browsing with Middleware

Directory browsing allows users of your web application to see the actual directory listing and files.

Directory browsing is disabled by default for security reasons.

Let’s look at an example in which we want to implement a list of images in a browser from the images folder in wwwroot. Middleware UseDirectoryBrowser can process and serve these images for this kind of request, and then close the rest of the pipeline.

app.UseDirectoryBrowser(new DirectoryBrowserOptions  
{  
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),  
    RequestPath = "/images"  
}); 

Summary

Middleware in ASP.NET Core controls how our application responds to HTTP requests.

Thus, each middleware component in ASP.NET Core:

  • Has access to both incoming requests and responses sent back.

  • Can simply pass the request to the next middleware in the pipeline.

  • Can execute some processing logic and then pass this request to the next middleware for further processing.

  • It can terminate (close) the request pipeline if necessary.

  • It is executed in the order in which it was added to the pipeline.

I hope you learned something from this article! Good luck with your learning!


You can learn more about the course and sign up for an open lesson here

Similar Posts

Leave a Reply Cancel reply