"
ASP.NET (snapshot 2017) Microsoft documentation and samples

ASP.NET Core Middleware Fundamentals

By Rick Anderson and Steve Smith

View or download sample code ((xref:)how to download)

What is middleware

Middleware is software that is assembled into an application pipeline to handle requests and responses. Each component:

Request delegates are used to build the request pipeline. The request delegates handle each HTTP request.

Request delegates are configured using Run, Map, and Use extension methods. An individual request delegate can be specified in-line as an anonymous method (called in-line middleware), or it can be defined in a reusable class. These reusable classes and in-line anonymous methods are middleware, or middleware components. Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline, or short-circuiting the chain if appropriate.

Migrating HTTP Modules to Middleware explains the difference between request pipelines in ASP.NET Core and the previous versions and provides more middleware samples.

Creating a middleware pipeline with IApplicationBuilder

The ASP.NET Core request pipeline consists of a sequence of request delegates, called one after the other, as this diagram shows (the thread of execution follows the black arrows):

Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the application. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, it’s handed back through the prior two middlewares for additional processing after the next() statements each in turn before leaving the application as a response to the client.
Request processing pattern showing a request arriving, processing through three middlewares, and the response leaving the application. Each middleware runs its logic and hands off the request to the next middleware at the next() statement. After the third middleware processes the request, it’s handed back through the prior two middlewares for additional processing after the next() statements each in turn before leaving the application as a response to the client.

Each delegate can perform operations before and after the next delegate. A delegate can also decide to not pass a request to the next delegate, which is called short-circuiting the request pipeline. Short-circuiting is often desirable because it avoids unnecessary work. For example, the static file middleware can return a request for a static file and short-circuit the rest of the pipeline. Exception-handling delegates need to be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline.

The simplest possible ASP.NET Core app sets up a single request delegate that handles all requests. This case doesn’t include an actual request pipeline. Instead, a single anonymous function is called in response to every HTTP request.

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Builder;
   2:  using Microsoft.AspNetCore.Hosting;
   3:  using Microsoft.AspNetCore.Http;
   4:   
   5:  public class Startup
   6:  {
   7:      public void Configure(IApplicationBuilder app)
   8:      {
   9:          app.Run(async context =>
  10:          {
  11:              await context.Response.WriteAsync("Hello, World!");
  12:          });
  13:      }
  14:  }
  15:   

The first app.Run delegate terminates the pipeline.

You can chain multiple request delegates together with app.Use. The next parameter represents the next delegate in the pipeline. (Remember that you can short-circuit the pipeline by not calling the next parameter.) You can typically perform actions both before and after the next delegate, as this example demonstrates:

[!code-csharpMain]

   1:  #define PrimarySample
   2:  #if PrimarySample
   3:  using Microsoft.AspNetCore.Builder;
   4:  using Microsoft.AspNetCore.Hosting;
   5:  using Microsoft.AspNetCore.Http;
   6:   
   7:  namespace Chain
   8:  {
   9:      #region snippet1
  10:      public class Startup
  11:      {
  12:          public void Configure(IApplicationBuilder app)
  13:          {
  14:              app.Use(async (context, next) =>
  15:              {
  16:                  // Do work that doesn't write to the Response.
  17:                  await next.Invoke();
  18:                  // Do logging or other work that doesn't write to the Response.
  19:              });
  20:   
  21:              app.Run(async context =>
  22:              {
  23:                  await context.Response.WriteAsync("Hello from 2nd delegate.");
  24:              });
  25:          }
  26:      }
  27:      #endregion
  28:  }
  29:  #endif

[!WARNING] Do not call next.Invoke after the response has been sent to the client. Changes to HttpResponse after the response has started will throw an exception. For example, changes such as setting headers, status code, etc, will throw an exception. Writing to the response body after calling next: - May cause a protocol violation. For example, writing more than the stated content-length. - May corrupt the body format. For example, writing an HTML footer to a CSS file.

HttpResponse.HasStarted is a useful hint to indicate if headers have been sent and/or the body has been written to.

Ordering

The order that middleware components are added in the Configure method defines the order in which they are invoked on requests, and the reverse order for the response. This ordering is critical for security, performance, and functionality.

The Configure method (shown below) adds the following middleware components:

  1. Exception/error handling
  2. Static file server
  3. Authentication
  4. MVC

ASP.NET Core 2.x

ASP.NET Core 1.x


In the code above, UseExceptionHandler is the first middleware component added to the pipeline—therefore, it catches any exceptions that occur in later calls.

The static file middleware is called early in the pipeline so it can handle requests and short-circuit without going through the remaining components. The static file middleware provides no authorization checks. Any files served by it, including those under wwwroot, are publicly available. See (xref:)Working with static files for an approach to secure static files.

ASP.NET Core 2.x

If the request is not handled by the static file middleware, it’s passed on to the Identity middleware (app.UseAuthentication), which performs authentication. Identity does not short-circuit unauthenticated requests. Although Identity authenticates requests, authorization (and rejection) occurs only after MVC selects a specific Razor Page or controller and action.

ASP.NET Core 1.x

If the request is not handled by the static file middleware, it’s passed on to the Identity middleware (app.UseIdentity), which performs authentication. Identity does not short-circuit unauthenticated requests. Although Identity authenticates requests, authorization (and rejection) occurs only after MVC selects a specific controller and action.


The following example demonstrates a middleware ordering where requests for static files are handled by the static file middleware before the response compression middleware. Static files are not compressed with this ordering of the middleware. The MVC responses from UseMvcWithDefaultRoute can be compressed.

Use, Run, and Map

You configure the HTTP pipeline using Use, Run, and Map. The Use method can short-circuit the pipeline (that is, if it does not call a next request delegate). Run is a convention, and some middleware components may expose Run[Middleware] methods that run at the end of the pipeline.

Map* extensions are used as a convention for branching the pipeline. Map branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.

[!code-csharpMain]

   1:  //#define Map
   2:  #if Map
   3:  using Microsoft.AspNetCore.Builder;
   4:  using Microsoft.AspNetCore.Hosting;
   5:  using Microsoft.AspNetCore.Http;
   6:   
   7:  #region snippet1
   8:  public class Startup
   9:  {
  10:      private static void HandleMapTest1(IApplicationBuilder app)
  11:      {
  12:          app.Run(async context =>
  13:          {
  14:              await context.Response.WriteAsync("Map Test 1");
  15:          });
  16:      }
  17:   
  18:      private static void HandleMapTest2(IApplicationBuilder app)
  19:      {
  20:          app.Run(async context =>
  21:          {
  22:              await context.Response.WriteAsync("Map Test 2");
  23:          });
  24:      }
  25:   
  26:      public void Configure(IApplicationBuilder app)
  27:      {
  28:          app.Map("/map1", HandleMapTest1);
  29:   
  30:          app.Map("/map2", HandleMapTest2);
  31:   
  32:          app.Run(async context =>
  33:          {
  34:              await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
  35:          });
  36:      }
  37:  }
  38:  #endregion
  39:  #endif

The following table shows the requests and responses from http://localhost:1234 using the previous code:

Request Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/map1 Map Test 1
localhost:1234/map2 Map Test 2
localhost:1234/map3 Hello from non-Map delegate.

When Map is used, the matched path segment(s) are removed from HttpRequest.Path and appended to HttpRequest.PathBase for each request.

MapWhen branches the request pipeline based on the result of the given predicate. Any predicate of type Func<HttpContext, bool> can be used to map requests to a new branch of the pipeline. In the following example, a predicate is used to detect the presence of a query string variable branch:

[!code-csharpMain]

   1:  //#define Map
   2:  #if Map
   3:  using Microsoft.AspNetCore.Builder;
   4:  using Microsoft.AspNetCore.Hosting;
   5:  using Microsoft.AspNetCore.Http;
   6:   
   7:  #region snippet1
   8:  public class Startup
   9:  {
  10:      private static void HandleBranch(IApplicationBuilder app)
  11:      {
  12:          app.Run(async context =>
  13:          {
  14:              var branchVer = context.Request.Query["branch"];
  15:              await context.Response.WriteAsync($"Branch used = {branchVer}");
  16:          });
  17:      }
  18:   
  19:      public void Configure(IApplicationBuilder app)
  20:      {
  21:          app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
  22:                                 HandleBranch);
  23:   
  24:          app.Run(async context =>
  25:          {
  26:              await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
  27:          });
  28:      }
  29:  }
  30:  #endregion
  31:  #endif

The following table shows the requests and responses from http://localhost:1234 using the previous code:

Request Response
localhost:1234 Hello from non-Map delegate.
localhost:1234/?branch=master Branch used = master

Map supports nesting, for example:

Map can also match multiple segments at once, for example:

Built-in middleware

ASP.NET Core ships with the following middleware components:

Middleware Description
(xref:)Authentication Provides authentication support.
(xref:)CORS Configures Cross-Origin Resource Sharing.
(xref:)Response Caching Provides support for caching responses.
(xref:)Response Compression Provides support for compressing responses.
(xref:)Routing Defines and constrains request routes.
(xref:)Session Provides support for managing user sessions.
(xref:)Static Files Provides support for serving static files and directory browsing.
(xref:)URL Rewriting Middleware Provides support for rewriting URLs and redirecting requests.

Writing middleware

Middleware is generally encapsulated in a class and exposed with an extension method. Consider the following middleware, which sets the culture for the current request from the query string:

[!code-csharpMain]

   1:  //#define Culture
   2:  #if Culture
   3:  using Microsoft.AspNetCore.Builder;
   4:  using Microsoft.AspNetCore.Hosting;
   5:  using Microsoft.AspNetCore.Http;
   6:  using System.Globalization;
   7:   
   8:  namespace Culture
   9:  {
  10:  #region snippet1
  11:      public class Startup
  12:      {
  13:          public void Configure(IApplicationBuilder app)
  14:          {
  15:              app.Use((context, next) =>
  16:              {
  17:                  var cultureQuery = context.Request.Query["culture"];
  18:                  if (!string.IsNullOrWhiteSpace(cultureQuery))
  19:                  {
  20:                      var culture = new CultureInfo(cultureQuery);
  21:   
  22:                      CultureInfo.CurrentCulture = culture;
  23:                      CultureInfo.CurrentUICulture = culture;
  24:                  }
  25:   
  26:                  // Call the next delegate/middleware in the pipeline
  27:                  return next();
  28:              });
  29:   
  30:              app.Run(async (context) =>
  31:              {
  32:                  await context.Response.WriteAsync(
  33:                      $"Hello {CultureInfo.CurrentCulture.DisplayName}");
  34:              });
  35:   
  36:          }
  37:      }
  38:  #endregion
  39:  }
  40:  #endif

Note: The sample code above is used to demonstrate creating a middleware component. See (xref:)Globalization and localization for ASP.NET Core’s built-in localization support.

You can test the middleware by passing in the culture, for example http://localhost:7997/?culture=no.

The following code moves the middleware delegate to a class:

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Http;
   2:  using System.Globalization;
   3:  using System.Threading.Tasks;
   4:   
   5:  namespace Culture
   6:  {
   7:      public class RequestCultureMiddleware
   8:      {
   9:          private readonly RequestDelegate _next;
  10:   
  11:          public RequestCultureMiddleware(RequestDelegate next)
  12:          {
  13:              _next = next;
  14:          }
  15:   
  16:          public Task Invoke(HttpContext context)
  17:          {
  18:              var cultureQuery = context.Request.Query["culture"];
  19:              if (!string.IsNullOrWhiteSpace(cultureQuery))
  20:              {
  21:                  var culture = new CultureInfo(cultureQuery);
  22:   
  23:                  CultureInfo.CurrentCulture = culture;
  24:                  CultureInfo.CurrentUICulture = culture;
  25:   
  26:              }
  27:   
  28:              // Call the next delegate/middleware in the pipeline
  29:              return this._next(context);
  30:          }
  31:      }
  32:  }

The following extension method exposes the middleware through IApplicationBuilder:

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Builder;
   2:   
   3:  namespace Culture
   4:  {
   5:      public static class RequestCultureMiddlewareExtensions
   6:      {
   7:          public static IApplicationBuilder UseRequestCulture(
   8:              this IApplicationBuilder builder)
   9:          {
  10:              return builder.UseMiddleware<RequestCultureMiddleware>();
  11:          }
  12:      }
  13:  }

The following code calls the middleware from Configure:

[!code-csharpMain]

   1:  #define Culture
   2:  #if Culture
   3:  using Microsoft.AspNetCore.Builder;
   4:  using Microsoft.AspNetCore.Hosting;
   5:  using Microsoft.AspNetCore.Http;
   6:  using System.Globalization;
   7:   
   8:  namespace Culture
   9:  {
  10:  #region snippet1
  11:      public class Startup
  12:      {
  13:          public void Configure(IApplicationBuilder app)
  14:          {
  15:              app.UseRequestCulture();
  16:   
  17:              app.Run(async (context) =>
  18:              {
  19:                  await context.Response.WriteAsync(
  20:                      $"Hello {CultureInfo.CurrentCulture.DisplayName}");
  21:              });
  22:   
  23:          }
  24:      }
  25:  #endregion
  26:  }
  27:  #endif

Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor. Middleware is constructed once per application lifetime. See Per-request dependencies below if you need to share services with middleware within a request.

Middleware components can resolve their dependencies from dependency injection through constructor parameters. UseMiddleware<T> can also accept additional parameters directly.

Per-request dependencies

Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method’s signature. The Invoke method can accept additional parameters that are populated by dependency injection. For example:

Resources



Comments ( )
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/fundamentals/middleware.htm
< THANKS ME>