Interpretation of ASP. NET 5 & MVC6 series (6): Explanation of Middleware and interpretation of ASP. NET
In Chapter 4 project structure analysis, we mentionedStartup.cs
As the entry point of the entire program, it is equivalent to the traditionalGlobal.asax
File: used to initialize system-level information (for example, the routing configuration in MVC ). In this chapter, we will analyze how to initialize system-level information here.
Pipeline differences between old and new versions
The biggest difference between ASP. NET 5 and earlier versions is the completely new rewriting of HTTP Pipeline. in earlier versionsHttpModule
Is a module component.HttpApplication
To implement authentication, global error processing, logs, and other functions. Traditional Form authentication isHTTPModule
.HTTPModule
Not only can filterRequest
AndResponse
Respond to interaction and modify. These HTTPModule components are inherited from the IHttpModule interface, which is located inSystem.Web.dll
.
The HttpModule code can be added in each event cycle in Global. asax. It can also be compiled into a class library separately and registered in web. config.
The new version of ASP. NET 5 abandoned the heavyweight System. Web. dll and introducedMiddleware
Concept,Middleware
The official definition is as follows:
Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose.
The Pipeline between the server and the application is interspersed with multiple Middleware components for specific purposes to check the request and response
Query, route, or modify.
This definition and the traditionalHttpModule
AndHttpHandler
Special image.
Middleware registration and Configuration
In ASP. NET5, the access to the request Pipeline (Pipeline) is carried out in the Startup class. This class is a convention class, andConfigureServices
Method,Configure
Methods and corresponding parameters are also agreed in advance, so they cannot be modified.
Dependency processing in Middleware: ConfigureServices Method
In ASP. various default Middleware in NET5 use the dependency injection function, so when using the features in Middleware, the types and mappings required for dependency injection must be registered to the dependency injection Management System in advance, that is, the IServiceCollection set. The ConfigureServices method receives a parameter of the IServiceCollection type, this parameter is a collection of all registered types. It is managed by the native dependency injection component (about ASP. dependency injection in NET5, which will be explained in a separate chapter). In this method, we can add a new type and type ing relationship to the set, for example:
// Add MVC services to the services container.services.AddMvc();
The code in the example is used to add the Service type related to the Mvc module to the system to support the MVC function. This method is an extension method used to add multiple MVC-related types to the collection.
Middleware registration and configuration: Configure Method
The Configure method signature is as follows:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory){ // ...}
Configure
The method receives three parameters:IApplicationBuilder
Type parameters are used to build the configuration information of the entire application,IHostingEnvironment
Classenv
Parameters are used to access content related to system environment variables,ILoggerFactory
Typeloggerfactory
Used for log-related content processing, whereIApplicationBuilder
Type parameters are the most important. The parameter instance app has a series of extension methods used to register various Middleware into the request Pipeline (Pipeline. The main difference between this method and the HTTP pipeline in ASP. NET is that the combined model in the new version replaces the event model in the old version. This requires that the new ASP. in NET, the Middleware component registration sequence is very important, because the latter component may need to use the previous component, so it must be registered in the order of dependency, for example, the template code example of the current MVC project is as follows:
// Add static files to the request pipeline.app.UseStaticFiles();// Add cookie-based authentication to the request pipeline.app.UseIdentity();// Add MVC to the request pipeline.app.UseMvc(routes =>{ /*...*/});
In the exampleUseStaticFiles
,UseIdentity
,UseMvc
AllIApplicationBuilder
In the extension method, the extension method is calledapp.UseMiddleware
Method, and then callapp.Use
Method to register a new Middleware. The method is defined as follows:
public interface IApplicationBuilder{ //... IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);}
The Code shows that middleware isFunc<RequestDelegate, RequestDelegate>
The Func receivesRequestDelegate
And returnsRequestDelegate
Type value.RequestDelegate
The source code is as follows:
public delegate Task RequestDelegate(HttpContext context);
Through the source code, we can see that,RequestDelegate
Is a delegate function that receivesHttpContext
Type instance, and returnsTask
Type of asynchronous object. That is to sayRequestDelegate
Is a response that can return itselfRequestDelegate
Type function, the entire ASP. NET is used to construct the pipeline (Pipelien). Here, each middleware is chained to the next middleware, And you canHttpConext
Object modification or maintenance, of course,HttpContext
Which includesHttpRequest
AndHttpResponse
Instance Object.
Note:HttpContext
,HttpRequest
,HttpResponse
ASP. NET 5 is a new type that is redefined.
Definition of Middleware
Since every middleare isFunc<RequestDelegate, RequestDelegate>
Is the definition of Middleware to meet a rule? Is it inherited from an abstract base class or an excuse? By looking at the relevant code, we can see that Middleware is defined based on the agreed form. The specific agreed rules are as follows:
The first parameter of the constructor must be the next processing function in the processing pipeline, that is, RequestDelegate. There must be an Invoke function that accepts the context parameter (HttpContent) and returns the Task;
Example:
Public class MiddlewareName {RequestDelegate _ next; public MiddlewareName (RequestDelegate next) {_ next = next; // receives incoming RequestDelegate instance} public async Task Invoke (HttpContext context) {// Process Code, such as processing context. request content Console. writeLine ("Middleware starts processing"); await _ next (context); Console. writeLine ("Middleware end processing"); // Process Code, such as processing context. content in Response }}
The template Code shows that a Middleware constructor needs to receive a RequestDelegate instance, save it to a private variable, and then callInvoke
Method (and receiveHttpContent
Instance) and returnsTask
AndInvoke
Inawait _next(context);
Statement, chained to the next Middleware, our processing code is mainly to execute the relevant code before and after the chain statement.
For example, to record the page execution time, first define a TimeRecorderMiddleware. The Code is as follows:
Public class TimeRecorderMiddleware {RequestDelegate _ next; public TimeRecorderMiddleware (RequestDelegate next) {_ next = next;} public async Task Invoke (HttpContext context) {var sw = new Stopwatch (); sw. start (); await _ next (context); var newDiv = @ "<div id =" process ""> page processing time: {0} millisecond </div> </body> "; var text = string. format (newDiv, sw. elapsedMilliseconds); await context. response. writeAsync (text );}}
There are many ways to register Middleware:
app.Use(next => new TimeRecorderMiddleware(next).Invoke);
Alternatively, you can use the UseMiddleware Extension Method for registration, for example:
App. UseMiddleware <TimeRecorderMiddleware> (); // app. UseMiddleware (typeof (TimeRecorderMiddleware); you can use either of the following methods:
Of course, you can also define your own extension method to register the Middleware. The Code is as follows:
public static IApplicationBuilder UseTimeRecorderMiddleware(this IApplicationBuilder app){ return app.UseMiddleware<TimeRecorderMiddleware>();}
Finally, register in the Configure method of the Startup class. The Code is as follows:
Public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) {app. useTimeRecorderMiddleware (); // put it in front for statistics. If it is placed behind Mvc, the time cannot be counted. // And so on}
Compile, restart, and access the page. You can see the running time prompt at the bottom of the page.
Use of common Middleware functions
App. UseErrorPage ()
In IHostingEnvironment. when EnvironmentName is Development, the error message is displayed, and the display type of the error message can be set through the extra ErrorPageOptions parameter, and all display can be set, you can also set to display only one or more Cookies, Environment, predictiondetails, Headers, Query, and SourceCode SourceCodeLineCount.
App. UseErrorHandler ("/Home/Error ")
Capture all program exception errors and redirect requests to the specified page for friendly prompts.
App. UseStaticFiles ()
You can use this Pipeline to process static files.
App. UseIdentity ()
Enable cookie-Based ASP. NET identity authentication to support Pipeline request processing.
Define Middleware using delegation directly
Because Middleware isFunc<RequestDelegate, RequestDelegate>
Therefore, we do not need to define a separate class.Startup
Class, you can use the delegate call method, for example:
Public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) {app. use (new Func <RequestDelegate, RequestDelegate> (next => content => Invoke (next, content ))); // others} // note the parameter private async Task Invoke (RequestDelegate next, HttpContext content) {Console. writeLine ("initialization component start"); await next. invoke (content); Console. writeLine ("Pipeline completed in the Next Step ");}
Make a simple Middleware base class
Although there are agreed methods, sometimes we often get confused during development and cannot figure out what the conventions are. Therefore, here we can define an abstract base class, in the future, all Middleware will inherit this abstract class and reload the Invoke method when defining it, so as to avoid forgetting the conventions. The Code is as follows:
/// <Summary> /// abstract base class /// </summary> public abstract class implements actmiddleware {protected RequestDelegate Next {get; set;} protected implements actmiddleware (RequestDelegate next) {this. next = next;} public abstract Task Invoke (HttpContext context) ;}/// <summary> /// example Middleware /// </summary> public class DemoMiddleware: abstractMiddleware {public DemoMiddleware (RequestDelegate next): base (next) {} public async override Task Invoke (HttpContext context) {Console. writeLine ("DemoMiddleware Start. "); await Next. invoke (context); Console. writeLine ("DemoMiddleware End. ");}}
The usage is the same as above.
Terminate a chain call or block all Middleware
In some cases, you may not need to continue to execute the statements based on certain conditions. Instead, you want to know your friends and return the results. In this case, you can ignoreawait next.Invoke(content);
Directly use the Response. WriteAsync method to output the content.
In addition, in some cases, you may need to implement functions similar to those of handler in earlier versions, that is, you may not frequently respond to Response directly by using any Pipeline. NET provides a run method for implementing this function, you only need to call the following code in the Configure method to achieve similar content output.
app.Run(async context =>{ context.Response.ContentType = "text/html"; await context.Response.WriteAsync("Hello World!");});
For content about ASP. NET 5 Runtime, visit: https://msdn.microsoft.com/en-us/magazine/dn913182.aspx
Legacy problems
In the Mvc project, all dependency injection types are obtained through the IServiceProvider instance. Currently, you can obtain the instance in the following form:
Var services = Context. RequestServices; // var services = app. ApplicationServices in the Controller; // Startup
After obtaining the instance, you can use the following method to obtain a type of object:
Var controller = (AccountController) services. GetService (typeof (AccountController); // you need to determine whether the obtained object is null.
If you reference the Microsoft. Framework. DependencyInjection namespace, you can also use the following three extension methods:
Var controller2 = (AccountController) services. getService <AccountController> (); // you can determine whether the obtained object is null. // If the obtained AccountController instance is null, the field throws an exception instead of returning nullvar controller3 = (AccountController) services. getRequiredService (typeof (AccountController); var controller4 = (AccountController) services. getRequiredService <AccountController> ();
Why? How can I obtain the HttpContext and IApplicationBuilder instances without Startup and Controller to use these dependency injection services?
How can I obtain an IApplicationBuilder instance?
Answer: In Startup, save the IApplicationBuilder instance to a variable in the singleton. Later, the whole site can be used.
How do I obtain an HttpContext instance?
Answer: Refer to dependency injection for common classes.
Reference: http://www.mikesdotnetting.com/article/269/asp-net-5-middleware-or-where-has-my-httpmodule-gone