The core framework of ASP. NET Web APIs is a message processing pipeline, which is an ordered combination of HttpMessageHandler. This is a duplex pipeline. The request message flows in from one end and is processed by all HttpMessageHandler in sequence. At the other end, the target HttpController is activated, the Action method is executed, and the response message is generated. The Response Message reversely flows into this pipeline and will also be processed by HttpMessageHandler one by one. This is an abstract pipeline independent of the boarding environment. How can we monitor and receive requests, and pass the received requests to the message processing pipeline for processing and send the responses generated by the pipeline back to the client through the network. This is a problem that must be solved by Web API hosting.
Directory
I. HttpMessageHandler
2. DelegatingHandler
Iii. HttpServer
Iv. HttpRoutingDispatcher
V. HttpControllerDispatcher
I. HttpMessageHandler
The message processing pipeline of ASP. NET Web APIs is composed of a group of HttpMessageHandler connected by the beginning and end. The reason why ASP. NET Web APIs are highly scalable is mainly due to the pipeline design. Although the ASP. NET Web API framework is designed to process requests and respond to requests, the processing policies used vary with specific scenarios.
It is impossible and unnecessary to create a "omnipotent" processor to meet all request processing needs. Instead, let a processor be only responsible for a single message processing function. In specific application scenarios, we can select the desired processor based on the specific message processing requirements and form a complete message processing pipeline. Here, the processor used to complete a single message processing function is HttpMessageHandler.
Here, "message processing" has two meanings: Processing request messages and processing response messages. HttpMessageHandler directly or indirectly inherits the abstract type HttpMessageHandler defined in the namespace "System. Net. Http. ASP. NET Web APIs use HttpRequestMessage and HttpResponseMessage to represent the request messages and response messages processed by the pipeline. Therefore, the definition of HttpMessageHandler is well understood.
1: public abstract class HttpMessageHandler: IDisposable
2 :{
3: public void Dispose ();
4: protected virtual void Dispose (bool disposing );
5: protected abstract Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken );
6 :}
As shown in the code snippet above, the abstract class HttpMessageHandler defines a protected abstract method SendAsync, which is an asynchronous method that uses the "parallel programming mode" for tasks, in the subsequent sections, we will see ASP.. NET Web APIs are basically defined in this way. For this SendAsync method, the request parameter indicates the request passed to the current HttpMessageHandler for processing. This is an HttpRequestMessage object. Another cancellationToken parameter is a CancellationToken object used to send cancel Operation signals. If you have a basic understanding of parallel programming in. NET, I believe this type will not be unfamiliar.
The processing of request messages and response messages is reflected in the SendAsync method. Specifically, request message processing is directly implemented in the SendAsync method, while Response Message Processing is implemented through the Task Object. The message processing pipeline composed of HttpMessageHandler and the "flow" of request messages and response messages in the pipeline can be basically shown in the right figure.
The abstract class HttpMessageHandler implements the IDisposable interface, which implements the Dispose method in a "standard" way. As shown in the following code snippet, when we call the Dispose method, HttpMessageHandler does not perform any resource recycling operations. When we inherit the custom HttpMessagHandler of this abstract class, we can implement the resource recycling operation in the rewritten Dispose method.
1: public abstract class HttpMessageHandler: IDisposable
2 :{
3: // other operations
4: public void Dispose ()
5 :{
6: this. Dispose (true );
7: GC. SuppressFinalize (this );
8 :}
9:
10: protected virtual void Dispose (bool disposing)
11 :{}
12 :}
2. DelegatingHandler
We can say that the ASP. NET Web API message processing pipeline is formed by a set of ordered HttpMessagHandler "first and last connections". The specific implementation of "pipeline concatenation" is achieved through the DelegatingHandler type. As the name implies, DelegatingHandler has the delegate function. After the message processing task is completed, it can delegate another HttpMessagHandler for subsequent processing. If this delegate is also a DelegatingHandler object, can it form a delegate chain? Isn't this delegated chain a message processing pipeline composed of DelegatingHandler?
As shown in the following code snippet, DelegatingHandler is an abstract class inherited from the HttpMessageHandler class. The entrusted HttpMessagHandler we mentioned above is represented by its attribute InnerHandler. DelegatingHandler overrides the abstract method SendAsync defined in its class to call the method with the same name as the InnerHandler attribute.
1: public abstract class DelegatingHandler: HttpMessageHandler
2 :{
3: protected internal override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken );
4: public HttpMessageHandler InnerHandler get; set ;}
5 :}
As mentioned above, if ASP. the message processing pipeline of the NET Web API is composed of DelegatingHandler (except HttpMessageHandler at the end of the pipeline). We can obtain the reference to the delegate HttpMessageHandler object based on its InnerHandler, this constitutes a chain structure as shown in. The message processing pipeline that forms the core framework of ASP. NET Web APIs is that simple.
Iii. HttpServer
In general, for ASP. for all HttpMessageHandler of the NET Web API message processing pipeline, except for the one at the end, all others are DelegatingHandler, so the "Next" HttpMessageHandler is maintained through the InnerHandler attribute. As the "Leader" of the HttpMessageHandler chain, an HttpServer object is defined under the namespace "System. Web. Http.
As shown in the following code snippet, HttpServer directly inherits from DelegatingHandler. It has two important read-only attributes (Configuration and Dispatcher). We can use the former to obtain the HttpConfiguration object used to configure the entire message processing pipeline, another property, Dispatcher, returns the HttpMessageHandler that is at the end of the entire message processing pipeline.
1: public class HttpServer: DelegatingHandler
2 :{
3: public HttpConfiguration Configuration {get ;}
4: public HttpMessageHandler Dispatcher {get ;}
5:
6: public HttpServer ();
7: public HttpServer (HttpMessageHandler dispatcher );
8: public HttpServer (HttpConfiguration configuration );
9: public HttpServer (HttpConfiguration configuration, HttpMessageHandler dispatcher );
10:
11: protected override void Dispose (bool disposing );
12: protected virtual void Initialize ();
13: protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken );
14 :}
The Configuration and Dispatcher attributes of HttpServer can be initialized in the corresponding constructor. If the values of these two attributes are not explicitly specified when constructing an HttpServer (the default no-argument constructor is called to create an HttpServer), an HttpConfiguration is created by default as the Configuration Attribute Value, the Dispatcher attribute value is an HttpRoutingDispatcher object, which is defined in the namespace "System. web. http. under Dispatcher. In addition. Because the HttpConfiguration type implements the IDisposable interface, HttpServer overwrites the virtual method Dispose and releases the HttpConfiguration object in this method.
After the HttpServer object is successfully created, the "one end at one end" of the corresponding message processing pipeline has been determined. One end refers to the HttpServer object, and the other end refers to the HttpMessageHandler object referenced by the Dispatcher attribute. ASP. the biggest extension of the NET Web API framework is that we can "customize" The message processing pipeline based on specific message processing needs, it allows us to "Install" the custom HttpMessageHandler to the end as shown in the left figure. But how do these HttpMessageHandler in the "middle position" register?
Since the entire pipeline is configured by HttpConfiguration, you can also use the custom HttpMessageHandler registration. As shown in the following code snippet, HttpConfiguration has a read-only set of MessageHandlers. The HttpMessageHandler to be registered must be added to this set. Because this is a set of DelegatingHandler element types, our custom HttpMessageHandler must inherit from DelegatingHandler.
1: public class HttpConfiguration: IDisposable
2 :{
3: // other members
4: public Collection MessageHandlers {get ;}
5 :}
Through the HTTP server type definition given above, we can see that it has a protected Initialize method, which finally completes the construction of the entire message processing pipeline. In the rewritten SendAsync method, if it is not initialized, the Initialize method is automatically called to ensure that the entire message processing pipeline has been successfully built.
Iv. HttpRoutingDispatcher
By default, an HttpRoutingDispatcher object is returned for the Dispatcher attribute of the HttpServer that acts as the "Leader" of the message processing pipeline. It can be regarded as the last HttpMessageHandler of the message processing pipeline. Web API call requests are generally for an Action method defined in an HttpController. Therefore, the message processing pipeline needs to activate the corresponding HttpController and execute the corresponding Action method, httpRoutingDispatcher completes the activation of the target HttpController and the execution of the Action method.
As shown in the following code snippet, HttpRoutingDispatcher is no longer the successor of DelegatingHandler, and its direct base class is the abstract class HttpMessageHandler. We need to specify an HttpConfiguration object when constructing an HttpRoutingDispatcher object. The HttpMessageHandler specified by the defaultHandler parameter is of great significance for the created HttpRoutingDispatcher object, because HttpController activation, Action Method Selection and execution, and other subsequent operations are actually completed by it.
1: public class HttpRoutingDispatcher: HttpMessageHandler
2 :{
3: public HttpRoutingDispatcher (HttpConfiguration configuration );
4: public HttpRoutingDispatcher (HttpConfiguration configuration, HttpMessageHandler defaultHandler );
5:
6: protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken );
7 :}
Although ASP. NET Web API message processing pipeline does not have an object similar to HttpContext to store context information based on the current request, but the HttpRequestMessage object indicating the request message has an attribute dictionary represented by the Properties attribute, we can use it to store context data as a container.
Through the previous introduction to HttpServer, we know that it will add the current SynchronizationContext and HttpConfiguration to the attribute dictionary that indicates the HttpRequestMessage object of the current request. Similarly, the HttpRouteData generated by the routing system is saved in the attribute Dictionary of HttpRequestMessage in the same way, we can directly call the following two extension methods of HttpRequestMessage, GetRouteData and SetRouteData, to obtain and set HttpRouteData.
1: public static class HttpRequestMessageExtensions
2 :{
3: // other members
4: public static IHttpRouteData GetRouteData (this HttpRequestMessage request );
5: public static void SetRouteData (this HttpRequestMessage request, IHttpRouteData routeData );
6 :}
When the SendAsync method of HttpRoutingDispatcher is executed, it determines whether such an HttpRouteData object exists in the attribute Dictionary of the HttpRequestMessage object as the parameter. If the HttpRouteData object exists, it will directly deliver the request to the specified HttpMessageHandler during creation for processing. This will happen in Web Host hosting mode.
If the HttpRouteData object that encapsulates route data has not been added to the attribute Dictionary of the HttpRequestMessage object indicating the request to be processed, it means that the route for the request has not occurred yet, this happens in Self Host mode. In this case, HttpRoutingDispatcher obtains the global route table directly through the Routes attribute of the current HttpConfiguration, and calls its GetRouteData method as a parameter to implement route resolution for the current request.
If you execute the GetRouteData method of the route table to return a specific HttpRouteData object, it means that the current request matches a registered HttpRoute. HttpRoutingDispatcher adds the HttpRouteData object to the attribute Dictionary of the HttpRequestMessage object. After that, ASP. NET Web API delivers the request to the specified HttpMessageHandler at creation for subsequent processing. If the GetRouteData method is executed and the return value is Null, it means that the current request does Not match the registered routing rule, and the client returns a response in the "404, Not Found" status.
V. HttpControllerDispatcher
From the type name, we can see that HttpRoutingDispatcher has two basic functions: "Routing" and "Dispatching )". For the former, it will call the current route table to implement route resolution for the request message and generate the HttpRouteData used to encapsulate the route data (if such HttpRouteData does not exist in the attribute Dictionary of the current request ). For the latter, it distributes requests directly to the specified HttpMessageHandler at the time of creation for further processing.
If the HttpMessageHandler object is not explicitly specified by the defaultHandler parameter when constructing the HttpRoutingDispatcher object, the HttpMessageHandler that takes over the request from it is an object with the following definitions, this type is defined in the namespace "System. web. http. under Dispatcher. HttpControllerDispatcher is particularly important in the entire message processing pipeline, because the activation of the target HttpController, the execution of the Action method, and the generation of the response are all completed by HttpControllerDispatcher.
1: public class HttpControllerDispatcher: HttpMessageHandler
2 :{
3: public HttpControllerDispatcher (HttpConfiguration configuration );
4: protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken );
5:
6: public HttpConfiguration Configuration {get ;}
7 :}
After we introduce the HttpControllerDispatcher object, the message processing pipeline of ASP. NET Web API will have the structure shown in the right figure. From this structure, it seems that HttpControllerDispatcher is the last HttpMessageHandler of the entire message processing pipeline. This statement is correct, but I personally prefer to regard HttpControllerDispatcher as an "internal" HttpMessageHandler "of" HttpRoutingDispatcher, therefore, the HttpRoutingDispatcher that "includes" HttpControllerDispatcher is regarded as the last HttpMessageHandler that makes up the message processing pipeline.
In addition, the chain structure like "N DelegagingHandler + 1 HttpMessageHander" also matches the delegate Chain Based on DelegagingHandler. For readers and friends, it is not important to have a deep understanding of the composition of the entire message processing pipeline.