Asp.net core mvc analysis: processing pipeline construction, coremvc
As mentioned in the startup process article, in the WebHost class, the http request processing pipeline is constructed through BuildApplication. Let's take a look at the Code:
...... // The Configure method configure (builder) in the Startup. cs class is called; // generate the middleware chain structure return builder. Build ();
In the Framework, a middleware processing logic is represented by a RequestDelegate delegate type, defined:Delegate Task RequestDelegate (HttpContext context)
Can we directly create the delegate method? The answer is no. In order to form a chain structure, the intermediate definition and registration have certain requirements.
First, we will introduce how to define a middleware. To define the middleware, you only need to define a class, but this class is not arbitrarily written. The structure in it is subject to certain requirements:
1. The class must contain a constructor. The first parameter of this constructor must be of the RequestDelegate type. This parameter represents the next middleware of the currently defined middleware.
2. It must contain an Invoke method. The first parameter of the method must be of the HttpContext type, and the return value must be of the Task type. The middleware logic is implemented in the Invoke method.
The following is a middleware definition instance code:
Class MiddlewareSample {private RequestDelegate _ next; public MiddlewareSample (RequesetDelegate next) {_ next = next;} public Task Invoke (HttpContext context) {// middleware logic code ...... // call the next middleware to implement chained calling. Of course, you can determine whether to continue calling _ next (context) according to the business scenario );}}
Let's take a look at how to register the defined middleware into the pipeline. IApplicationBuilder provides the UseMiddleware extension method, through which a middleware type can be registered to the pipeline. Let's take a look at what it actually does?
Public static IApplicationBuilder UseMiddleware (this IApplicationBuilder app, Type middleware, params object [] args) {var applicationServices = app. applicationServices; return app. use (next => {var methods = middleware. getMethods (BindingFlags. instance | BindingFlags. public); // obtain the Invoke method information through reflection var invokeMethods = methods. where (m => string. equals (m. name, InvokeMethodName, StringComparison. ordinal )). ToArray (); if (invokeMethods. length> 1) {throw new InvalidOperationException (Resources. formatException_UseMiddleMutlipleInvokes (InvokeMethodName);} if (invokeMethods. length = 0) {throw new InvalidOperationException (Resources. formatException_UseMiddlewareNoInvokeMethod (InvokeMethodName);} var methodinfo = invokeMethods [0]; if (! Typeof (Task ). isAssignableFrom (methodinfo. returnType) {throw new InvalidOperationException (Resources. formatException_UseMiddlewareNonTaskReturnType (InvokeMethodName, nameof (Task);} var parameters = methodinfo. getParameters (); // determines whether the Invoke method contains the HttpContext parameter and must be the first parameter if (parameters. length = 0 | parameters [0]. parameterType! = Typeof (HttpContext) {throw new InvalidOperationException (Resources. formatException_UseMiddlewareNoParameters (InvokeMethodName, nameof (HttpContext);} var ctorArgs = new object [args. length + 1]; ctorArgs [0] = next; Array. copy (args, 0, ctorArgs, 1, args. length); // instantiate the middleware type and pass the first parameter of the next most constructor to var instance = ActivatorUtilities. createInstance (app. applicationServices, middleware, ctorArgs); if (Parameters. length = 1) {// If the invoke method contains only one httpcontext parameter, directly create RequestDelegate return (RequestDelegate) methodinfo. createDelegate (typeof (RequestDelegate), instance);} // here I understand it as a RequestDelegate proxy delegate var factory = Compile <object> (methodinfo, parameters ); return context => {var serviceProvider = context. requestServices ?? ApplicationServices; if (serviceProvider = null) {throw new InvalidOperationException (Resources. formatException_UseMiddlewareIServiceProviderNotAvailable (nameof (IServiceProvider);} return factory (instance, context, serviceProvider );};});}
In the UseMiddleware method, We parse the types defined by the middleware through reflection, and finally create a middleware factory delegate object. The factory is a Func <RequestDelegate, RequestDelegate> delegate type, it receives a RequestDelegate middleware and returns a new middleware. The received middleware parameter is the next middleware of the newly generated middleware. After the factory is created, call the IApplicationBuilder. Use method to add the factory to the middleware factory list. The Use method is implemented as follows:
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; }
This is only a collection of middleware factories. Finally, the middleware chain structure is generated by calling the builder. Build () method.
public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return TaskCache.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }
First, we define a RequestDelegate that returns a 404 error exception. This is used as the middleware, and then the middleware set is reversed, and the middleware factory is called in turn to complete the initialization of the middleware. Here is why we need to reverse it. Let's analyze it.
For example, if the order of middleware registration is 1-> 2-> 3-> 4, the order of the factory in _ components is factory 1-> factory 2-> factory 3-> Factory 4, after the reversal, it becomes Factory 4-> factory 3-> factory 2-> Factory 1, and then enters the for loop. First, call Factory 4 and use the 404 error middleware as the parameter, finally, a middleware 4 is returned, and factory 3 is called. Middleware 4 is passed in to generate a middleware, and factory 1 is called, this forms a chain structure such as middleware 1-> middleware 2-> middleware 3-> middleware 4-> 404 error middleware. When an http request comes in, the corresponding middleware logic is executed in sequence.