How to Configure ASP.NET Core Middleware for Different Types of Request

In ASP.NET Core, middleware is merely the process for managing web requests. Your applications are essentially simply a collection of middleware because all ASP.NET Core applications require at least one piece of middleware to respond to requests. Middleware includes even the MVC pipeline itself.

There is an Invoke function with a HttpContext argument for each middleware component. If appropriate, you can utilize this parameter to process the request and produce a response:

public async Task Invoke(HttpContext context)
{
    if (context.Request.Path...)
    {
        await context.Response.WriteAsync("writing text...");
    }
}

You can add more arguments to your Invoke method at your discretion, and the built-in dependency injection support will resolve these dependencies on each request (these dependencies would need to be registered in ConfigureServices):

public async Task Invoke(HttpContext context, MyOtherDependency blah)
{
    if (blah.DoSomething(context))
    {
        ...
    }
}

Middleware Execution

It’s vital to understand that the order of registration influences the order of execution even though we’ll learn how to setup the middleware pipeline in the following section.

For each request, your application’s top-level middleware will always be called. The framework carries this out automatically. This middleware has two options: it can either call the following piece of middleware or respond to the client, closing the pipeline. Of course, it needs access to the subsequent middleware component for the latter choice.

For this reason, the constructor of the majority of middleware components is defined to accept a RequestDelegate argument.

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    
    public MyMiddleware(RequestDelegate next, OtherDependencies...)
    {
        _next = next;
    }
}

You receive a reference to the following piece of middleware in the pipeline when the RequestDelegate is automatically filled in. The RequestDelegate is normally stored so that it may be called from the Invoke method, which is where the middleware performs its functions.

The Ins and Outs of Invoke

The Invoke method of a middleware typically has the following structure, which is intriguing:

public async Task Invoke(HttpContext context)
{
    // code executed before the next middleware
    ...
    await _next.Invoke(context); // call next middleware

    // code executed after the next middleware
    ...
}

The in phase, which comes before the next middleware is called, and the out phase, which comes after, give each piece of middleware two opportunities to perform some job.

It’s also crucial to understand that a middleware component does not always need to call the one after it. For instance, you might have middleware that looks for an API key; if the key is missing, you might want to publish a response right away and bypass the rest of the pipeline. After all, if the user is not verified, there is probably no need to carry out your secure controller operations.

You normally call the subsequent middleware component or write a response when developing middleware. You shouldn’t ever or seldom ever do both.

You can find out more about middleware in the ASP.Net Core docs.

Registering middleware with Use*

Middleware is registered in Startup.cs’s Configure function. The IApplicationBuilder argument of the Configure method offers the methods required for all forms of middleware registration through a number of extension methods.

UseMiddleware is the standard method for registering middleware that is applicable to all requests:

public void Configure(IApplicationBuilder app, ...)
{
    app.UseMiddleware<MyCustomMiddleware>();
}

Since middleware authors typically provide extension methods particular to the middleware being registered, you hardly ever need to call UseMiddleware directly:

public void Configure(IApplicationBuilder app, ...)
{
    app.UseMyCustomMiddleware();
}

Normally, the extension method does nothing more than register the middleware with UseMiddleware in the background.

public static class MyCustomMiddlewareExtensions
{
    public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder app)
    {
        app.UseMiddleware<MyCustomMiddleware>();

        return app;
    }
}

Because our middleware is so straightforward, adopting the custom extension approach is only marginally more readable and user-friendly than the original UseMiddleware approach in our example. In contrast to UseMiddleware’s params object[] args approach, most real-world middleware supports some configuration, and bespoke extensions methods can offer a far friendlier programming style.

The accepted method for registering middleware that applies to all requests is to use extensions methods. We’ll examine scenarios where you might want to use a different middleware pipeline for specific requests in the following section.

Middleware branching with MapWhen

By supplying a predicate, MapWhen enables you to divide the middleware pipeline into two entirely different branches:

app.UseMiddlewareOne();

app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
{
    appBuilder.UseMiddlewareTwo();
});

app.UseMiddlewareThree();

Middleware One in this illustration will always run. Assuming there is no short-circuiting (see above), middleware Two will run if the request path begins with “/api.” Middleware Three will run in the absence of that. This option prevents middleware Two and middleware Three from running simultaneously for a single request.

I don’t use MapWhen very much, but as an illustration, let’s say we want to prevent the StaticFiles middleware from being used for every request and only use it when a specific path is specified:

app.MapWhen(context => context.Request.Path.Value.StartsWithSegments("/assets"), 
    appBuilder => appBuilder.UseStaticFiles());

This would lead to the static files middleware only operating for request routes beginning with ‘/assets’ (where we store our static files). The next registered middleware would run straight in the event that a request had a path that did not meet this criterion, potentially saving crucial nanoseconds.

Conditional middleware with UseWhen

The last situation I’d like to discuss is when you want the majority of your middleware to execute for each request, but you have certain conditional pieces—certain middleware that should only execute for particular requests.

UseWhen, which also uses a predicate to decide whether the middleware should execute, makes this simple to accomplish:

app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
{
    appBuilder.UseStatusCodePagesWithReExecute("/apierror/{0}");

    appBuilder.UseExceptionHandler("/apierror/500");
});

When the request is for the API (identified by the URL), this code utilizes a different error handling middleware. You can conditionally apply middleware based on cookies, headers, the current user, and a lot more factors with the predicate method.

All middleware registered prior to the UseWhen function will be applicable to all requests, just like with MapWhen.

The way later middleware (i.e., those registered below) is run is the main distinction between UseWhen and MapWhen. In contrast to MapWhen, UseWhen keeps running subsequent middleware whether the UseWhen predicate was true or false.

Let’s modify our illustration and employ UseWhen:

app.UseMiddlewareOne();

app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
{
    appBuilder.UseMiddlewareTwo();
});

app.UseMiddlewareThree();

This time, middleware One and Three will be used in every situation, providing there is no short-circuiting. Only requests with the ‘/api’ request path will cause Middleware Two to be performed.

UseWhen is a highly potent and helpful tool. We can govern middleware by selecting how we register it, rather than the middleware determining whether it should execute (perhaps through configuration).

Here are a few illustrations:

  • Restrict output caching to anonymous users.
  • Add diagnostic headers for a certain IP subnet
  • Handle errors differently for API and MVC actions
  • Restrict certain requests from analytics

Conclusion

This post examined the many configuration options for the startup.cs file’s ASP.NET Core middleware. We demonstrated the typical method for registering middleware, which causes the middleware to run in response to each request. Then, we discussed two additional strategies that let you choose which middleware is used for each request. The majority of the time, you will register the middleware for all requests as is typical procedure, but it is very helpful to be aware that there are other options for unique circumstances.

Are you looking for an affordable and reliable Asp.net core hosting solution?

Get your ASP.NET hosting as low as $1.00/month with ASPHostPortal. Our fully featured hosting already includes

  • Easy setup
  • 24/7/365 technical support
  • Top level speed and security
  • Super cache server performance to increase your website speed
  • Top 9 data centers across the world that you can choose.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *