Middleware in ASPNET Core

This tutorial explains everything necessary to get you started with the middleware in ASPNET Core. We explain the built-in middleware components as well as the Use, Map and Run extension methods through practical code.
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

Terminology

App Pipeline:
When an http request comes from the browser it is processed through a chain of function calls. Each function performs some work and calls the next one till an http response is sent to the client. The chain of function calls is called the pipeline.
Middleware:
Each piece of code/function in the app pipeline is called middleware.
Short-circuiting
When a function doesn't call the next one. For example, a static file middleware processes a request for a static file, and short-circuits the pipeline.
Terminal Middleware:
The middleware that doesn't invoke any successor is called the terminal middleware. Most commonly it is the last component. But any component that short-circuits the request pipeline is a terminal middleware. Short circuiting can, for example, be done by an authorization component.
RequestDelegate delegate:
A function that accepts an HttpContext as one of its parameters and returns a Task. Request delegates handle each http request. In the example below the Run [extension method] calls an anonymous inline RequestDelegate.
public void Configure(IApplicationBuilder app)
{

  app.Run(async context =>

  {

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

  });

}

Simplest App - Just one Terminal Middleware

Run is an extension method on IApplicationBuilder that adds a terminal middleware delegate to the pipeline.

Following is the simplest aspnet core app with just one terminal middleware that writes Hello, World to any request that comes in.

public class Startup
{

  public void Configure(IApplicationBuilder app)
  {

    app.Run(async context =>

    {

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

    });

  }

}

App with two Middleware

"Use" is an extension method that can chain request delegates together.

Use method receives a "next" parameter for chaining.

public class Startup
{

  public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
  {

    StringBuilder sbResponse = new StringBuilder();

    // first component 
    app.Use(async (context, next) =>

    {

      // Do work before passing to next 
      sbResponse.Append("Hi . . .");

      // call the next delegate in the pipeline 
      await next();

      // some work after 
      logger.LogInformation("my log info");

    });

    // terminal 
    app.Run(async context =>

    {

      sbResponse.Append("Hello, World!");

      await context.Response.WriteAsync(sbResponse.ToString());

    });

  }

}

Branching a Middleware with Map

Map extensions are used to branch the request pipeline based on matches of the request path.

public class Startup
{

  public void Configure(IApplicationBuilder app)
  {

    app.Map("/map1", (app) =>

    {

      app.Run(async context =>

      {

        await context.Response.WriteAsync("map 1");

      });

    }) ;

    app.Map("/map2", (app) =>

    {

      app.Run(async context =>

      {

        await context.Response.WriteAsync("map 2");

      });

    });

    app.Run(async context =>

    {

      await context.Response.WriteAsync("non-map");

    });

  }

}

Branching with UseWhen and MapWhen

They branch the request pipeline based on the results of a predicate.

IMPORTANT: UseWhen doesn't stop executing the rest of the pipeline when the delegate returns true. So "next()" is implied.

public void Configure(IApplicationBuilder app)
{

  StringBuilder sb = new StringBuilder();

  // predicate based use 
  app.UseWhen(context => context.Request.IsHttps, app =>

  {

    sb.Append("Inside UseWhen . . .");

    // no need to call next 
    // automatically calls next 
  });

  // predicate based branching 
  app.MapWhen(context => context.Request.IsHttps, app =>

  {

    app.Run(async context =>

    {

      sb.Append("and Map When!");

      await context.Response.WriteAsync(sb.ToString());

    });

  });

}

Middleware Components provided by the ASP.NET Core library

Following are some of the middleware components:

  1. UseDeveloperExceptionPage()
  2. UseHsts()
    (HTTP Strict Transport Security Protocol)
  3. UseHttpsRedirection()
  4. UseStaticFiles()
    returns static files and short-circuits further request processing.
  5. UseRouting()
    to route requests
  6. UseAuthentication()
  7. UseSession()
  8. UseEndPoints()
    (terminal for matching routes)

Typical Middleware

Following is the recommended order. The order is critical for security, performance, and functionality.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

  if (env.IsDevelopment())
  {

    app.UseDeveloperExceptionPage();

    app.UseDatabaseErrorPage();

  }

  else 
  {

    app.UseExceptionHandler("/Error");

    app.UseHsts();

  }

  app.UseHttpsRedirection();

  app.UseStaticFiles();

  // app.UseCookiePolicy(); 

  app.UseRouting();

  // app.UseRequestLocalization(); 

  // Cross-Origin Resource Sharing 
  // app.UseCors(); 

  app.UseAuthentication();

  app.UseAuthorization();

  // app.UseSession(); 

  // app.UseResponseCaching(); 

  app.UseEndpoints(endpoints =>
  {

    endpoints.MapRazorPages();

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

  });

}

Notes on UseEndpoints

  1. Must be used with UseRouting()
  2. used to add endpoints for requests based on http request type [Post, Get, Put, etc.,] and on the matched route.
  3. used to add endpoints for razor pages.
  4. used to add endpoints for matched controller routes.
  5. and so on

Following is an example:

public class Startup
{

  public void ConfigureServices(IServiceCollection services)
  {

    services.AddMvc();

  }

  public void Configure(IApplicationBuilder app)
  {

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {

      endpoints.MapGet("/", async context =>
      {

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

      });

      // post requests 
      endpoints.MapPost("/", async context =>
      {

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

      });

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

    });

  }

}

Exercises on Middleware

for solutions please join our Course on ASP.NET Core with Project ("Find a Doctor")
  1. Without using razor pages, write a simple ASP.NET Core application that writes a "Hello World!" message to the response stream when an HTTP GET request is made to the home page of your website.
  2. Without using razor pages, write a simple ASP.NET Core application that sends a FORM consisting of an input textbox and a submit when an HTTP GET request is made to the home page of your website. The application should handle the posted data and respond by sending back the upper case equivalent of the input typed by the user.

Comments and Discussion

Please write your comments and discussion on the following youtube video itself:


This Blog Post/Article "Middleware in ASPNET Core" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.