What is the middleware (Middleware)?
Middleware is assembled to the conduit application software to handle requests and responses. Each component:
- Choose whether to pass the request to the next pipeline component.
- You can call before the next component in the pipeline and after implementation.
Delegates the request (Request delegates) for constructing the request pipeline, each HTTP request processing.
Use delegation request Run
, Map
and Use
is configured extension method. Separate request can delegate inline anonymous method (referred to as inline middleware) is specified, or it may be defined in the reusable class. These reusable classes and anonymous method is inline middleware, or a middleware component. Each middleware component requests processes are responsible for calling the next component in the pipeline, if appropriate, is responsible for linking short-circuit.
HTTP module to migrate the middleware explains the differences between the previous version and ASP.NET Core (ASP.NET) in the request pipeline, middleware and provides additional examples.
Use IApplicationBuilder create middleware pipeline
ASP.NET Core request process by a series of requests and entrusted, as shown (black arrows execution flow follows):
Each delegate can delegate and before the next operation after execution. Delegate may also decide not to delegate a request to the next, which is called a short-circuit conduit request. Short circuit is usually preferable because it avoids unnecessary work. For example, the middleware static files may return the request for a static file, the short-circuit and the rest of the pipe. You need to call exception handling pipeline commissioned in early so they can catch exceptions behind pipe.
The easiest is probably ASP.NET Core application request to establish a commission to handle all the requests. This case does not contain the actual request pipeline. Instead, for each HTTP request to call an anonymous method.
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; public class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); }); } }
The first app.Run
commission expires pipeline.
The following code:
Access through the browser and found that in the first app.Run
termination of the pipeline.
You can delegate the request to a plurality of app.Use
joined together. next
Parameter indicates a delegate conduit. (Remember, you can not call the next parameter to the end of the line.) Can usually be commissioned before and after the next operation is performed in the following example:
public class the Startup { public void the Configure (IApplicationBuilder App) { app.Use ( the async (context, Next) => { the await context.Response.WriteAsync ( " before entering the next delegate performing a first delegate \ R & lt \ n- " ); // call the next pipe delegate the await next.Invoke (); the await context.Response.WriteAsync ( " after the end of the first delegate to the next delegate \ R & lt \ n- " ); }); app.run ( the async context => { the awaitcontext.Response.WriteAsync ( " into the second delegate \ R & lt \ n- " ); the await context.Response.WriteAsync ( " the Hello from 2nd \ R & lt \ n-the delegate. " ); the await context.Response.WriteAsync ( " end of the second a delegate \ R & lt \ n- " ); }); } }
Use a browser to access the following results:
It can be seen that the order of execution of the request delegation of following the above flow chart.
Note: The
response is sent back to the client, do not call next.Invoke
. After the start of the response, changes to the HttpResponse will throw an exception. For example, in response to setting the first status code and other changes will throw an exception. In the call next
after writing the response body.
-
It may lead to protocol violations. For example, writing over
content-length
the content length. -
It may destroy the contents of the response format. For example, the HTML footer write CSS file.
HttpResponse.HasStarted is a useful tips, sending a response indicating whether the head and / or written text.
order
In Startup。Configure
order to add a middleware component calls a method defined in their order, and in reverse order in response to the request. This ordering is essential for safety, performance and functionality.
Startup.Configure
The method (shown below) were added the following middleware components:
- Exception / Error Handling
- Static files
- Authentication
- MVC
public void Configure(IApplicationBuilder app) { app.UseExceptionHandler("/Home/Error"); // Call first to catch exceptions // thrown in the following middleware. app.UseStaticFiles(); // Return static files and end pipeline. app.UseAuthentication(); // Authenticate before you access // secure resources. app.UseMvcWithDefaultRoute(); // Add MVC to the request pipeline. }
The above code, UseExceptionHandler
is added to the first conduit in a middleware component, so it captures any abnormality occurs in later calls.
Intermediate file called a static pipe in advance, it is possible to process the request and short-circuit, without passing through the remainder of the assembly. Static files middleware does not provide authorization checks. Any documents provided by including under wwwroot files are public.
If the request is not a static document processing middleware, which will be passed to the implementation of Identity Authentication middleware (app.UseAuthentication). Identity does not make unauthenticated request a short circuit. Although only occurs after authentication request, but the authorization (and refused) to select only specific pages or Razor controller and operator in MVC.
It will take place after authorization (and refused) to select only specific pages or Razor and Action Controller in MVC.
The following example demonstrates the middleware sequence, wherein the static file request before the response is processed by the static compression middleware intermediate file. Static files are not compressed in the order of middleware. MVC from UseMvcWithDefaultRoute response may be compressed.
public void Configure(IApplicationBuilder app) { app.UseStaticFiles(); // Static files not compressed app.UseResponseCompression(); app.UseMvcWithDefaultRoute(); }
Use, Run, 和 Map
You may be used Use
, Run
and the Map
configuration HTTP pipeline. Use
The method of the duct may be short-circuited (i.e., a request may not delegate the call). Run
It is a convention, and some middleware component may be exposed at the end of the pipe run Run [the Middleware] Method. Map*
Conventions used to extend branch ducts. The map matching a given request path to a branch request pipeline, if the request to start the path given path, the branch is executed.
public class Startup { private static void HandleMapTest1(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Map Test 1"); }); } private static void HandleMapTest2(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Map Test 2"); }); } public void Configure(IApplicationBuilder app) { app.Map("/map1", HandleMapTest1); app.Map("/map2", HandleMapTest2); app.Run(async context => { await context.Response.WriteAsync("Hello from non-Map delegate. <p>"); }); } }
The following table shows the use of the previous code http: // localhost: 19219 request and response:
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 using the Map, the matching segment from the path HttpRequest.Path
is deleted, and appended to each request Http Request.PathBase
.
MapWhen
The results given predicate request pipeline branch. Any type Func<HttpContext,bool>
of verbs can be used to map the request to the new branch conduit. In the following example, the predicate for detecting the presence of branched query string variables:
public class Startup { private static void HandleBranch(IApplicationBuilder app) { app.Run(async context => { var branchVer = context.Request.Query["branch"]; await context.Response.WriteAsync($"Branch used = {branchVer}"); }); } public void Configure(IApplicationBuilder app) { app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch); app.Run(async context => { await context.Response.WriteAsync("Hello from non-Map delegate. <p>"); }); }
The following table shows the use of the above code http: // localhost: 19219 request and response:
request | response |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=1 | Branch used = master |
Map
Supports nested, for example:
app.Map("/level1", level1App => { level1App.Map("/level2a", level2AApp => { // "/level1/level2a" //... }); level1App.Map("/level2b", level2BApp => { // "/level1/level2b" //... }); });
Map
A match may be a plurality of segments, for example:
app.Map("/level1/level2", HandleMultiSeg);
Built-in middleware
ASP.NET Core comes with the following middleware components:
Middleware | description |
---|---|
Authentication | It provides authentication support |
HEARTS | Configuring Cross-Origin Resource Sharing |
Response Caching | Provide support for cached responses |
Response Compression | Provide compression support response |
Routing | Request routing definitions and constraints |
Session | Provide user session management |
Static Files | Provide support for static file serving and directory browsing |
URL Rewriting Middleware | For rewriting Url, and redirection request support |
Write middleware
Middleware typically packaged in a class, and the method using the extended exposure. Review the following middleware, it sets Culture current request from the query string:
public class Startup { public void Configure(IApplicationBuilder app) { app.Use((context, next) => { var cultureQuery = context.Request.Query["culture"]; if (!string.IsNullOrWhiteSpace(cultureQuery)) { var culture = new CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; } // Call the next delegate/middleware in the pipeline return next(); }); app.Run(async (context) => { await context.Response.WriteAsync( $"Hello {CultureInfo.CurrentCulture.DisplayName}"); }); } }
You can test by passing Culture middleware, such as http: // localhost: 19219 / culture = zh-CN?
The following code is moved to a middleware delegate class:
using Microsoft.AspNetCore.Http; using System.Globalization; using System.Threading.Tasks; namespace Culture { public class RequestCultureMiddleware { private readonly RequestDelegate _next; public RequestCultureMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { var cultureQuery = context.Request.Query["culture"]; if (!string.IsNullOrWhiteSpace(cultureQuery)) { var culture = new CultureInfo(cultureQuery); CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; } // Call the next delegate/middleware in the pipeline return this._next(context); } } }
The following intermediate is exposed by extending the method IApplicationBuilder
using Microsoft.AspNetCore.Builder; namespace Culture { public static class RequestCultureMiddlewareExtensions { public static IApplicationBuilder UseRequestCulture( this IApplicationBuilder builder) { return builder.UseMiddleware<RequestCultureMiddleware>(); } } }
The following code from the Configure
call middleware:
public class Startup { public void Configure(IApplicationBuilder app) { app.UseRequestCulture(); app.Run(async (context) => { await context.Response.WriteAsync( $"Hello {CultureInfo.CurrentCulture.DisplayName}"); }); } }
Middleware should follow the principle explicit dependence, by its constructor expose dependencies. Middleware built once in the application life cycle. If you need to share services and middleware in the request, please refer to the following requests relevance.
Middleware component dependency may be resolved by the constructor dependency injection method parameters. UseMiddleware can also directly accept other parameters.
Each request dependencies
Because middleware is built upon application startup, instead of each request, so during each request, the scope of lifecycle services middleware constructor injection is not shared with other types of dependence. If you must share a scope service between middleware and other types, please add these services to the signature Invoke method. Invoke method may accept other injection parameters by the dependency filled. E.g:
public class MyMiddleware { private readonly RequestDelegate _next; public MyMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext, IMyScopedService svc) { svc.MyProperty = 1000; await _next(httpContext); } }