In the previous article, we briefly introduced HttpActionSelector for implementing the Action selection mechanism. Next we will discuss the most important content of this chapter: ASP. NET Web API how to use HttpActionSelector (taking the default ApiControllerActionSelector as an example) How to Select a matching Action method to process the current request after the target HttpController is successfully activated. [This Article has been synchronized to How ASP. NET Web API Works?]
Directory
List of candidate actions
Filters Action names in HttpRouteData.
Filter supported HTTP methods
Filter query strings and route Variables
Filter ActionMethodSelector
Exception Handling
List of candidate actions
For ease of understanding, we use a specific example to implement the Action selection policy in the SelectAction method of the ApiControllerActionSelector. Assume that the preceding DemoController inherited from ApiController is defined in an ASP. NET Web API application.
DemoController : ApiController
{
Get()
{
;
}
[NonAction]
[HttpGet]
[ActionName()]
Retrieve()
{
;
}
Get( x)
{
;
}
Get( x, y)
{
;
}
Get( x, y)
{
;
}
Put()
{
;
}
Post()
{
;
}
Delete()
{
;
}
}
DemoController defines a total of eight Action methods, all of which return strings representing the signatures of their respective methods. The first two methods Get and Retrieve do not have parameters. The Retrieve method applies three features: HttpGetAttribute to support HTTP-GET requests, ActionNameAttribute sets the Action name to "Get ", the NoActionAttribute feature makes it unable to be called directly through requests. The 3rd Get methods have a string type parameter x. Both the 4th and 5th Get methods have two parameters x and y with the same name. In addition to the five Action methods based on HTTP-GET requests, we also defined three corresponding Action methods for HTTP-PUT, HTTP-POST, and HTTP-DELETE. The eight Action Methods and Their supported HTTP methods are listed in the following table.
Suppose we directly use the route registration provided by default to create an ASP. NET Web API project. The relevant code is as follows. Next, we use different URLs to access the DemoController and analyze how the ApiControllerActionSelector filters out the target Action method for each specific request.
WebApiConfig
{
Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name : ,
routeTemplate : ,
defaults : { id = RouteParameter.Optional }
);
}
}
Filters Action names in HttpRouteData.
We know that HttpActionSelector has two basic methods. GetActionMapping returns the httping of HttpActionDescriptor and Action names of all Action methods defined in the target HttpController. Specifically, this method returns an ILookup <string, HttpActionDescriptor> object, which has the structure shown in the table above. The SelectAction method uses this ing table as the initial candidate HttpActionDescriptor list, and finally obtains the one used to process the current request through multiple rounds of filtering.
ApiControllerActionSelector filters the Action names provided by the request in the first round. Specifically, it first extracts the HttpRouteData object used to encapsulate route data from the specified HttpControllerContext. if it contains the name of the target Action (the Key of the corresponding entry is ""), it filters out the HttpActionDescriptor list that matches the Action name from the candidate items. Otherwise, if HttpRouteData does not contain the name of the target Action, this filtering will be ignored.
In this example, the generated HttpRouteData does not contain the name of the target Action because the variable for the Action name is not defined in the URL template during route registration.
WebApiConfig
{
Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name : ,
routeTemplate : /{id}",
defaults : { id = RouteParameter.Optional }
);
}
}
If we use the above method to make the URL template for Route registration contain a variable "{Action}" for the target action name }", for a request whose URL is "/api/contacts/get", ASP. the HttpRouteData generated by the routing system of the NET Web API will contain the name of the target Action "get". Then, the five actions with the same name shown in the right figure will be filtered out first.
Filter supported HTTP methods
The 2nd round of filtering by ApiControllerActionSelector is an HTTP method supported by candidate actions. Specifically, it obtains a list of HTTP methods supported by the corresponding Action through the attributes of the candidate HttpActionDescriptor object. If the list contains the HTTP Method of the current request, the HttpActionDescriptor will be filtered out.
For the eight Action methods defined in DemoController, if the request uses an HTTP method as the HTTP-GET, the five actions shown in the right figure that uniquely support the HTTP-GET request will be filtered out; if you are receiving an HTTP-PUT, HTTP-POST, or HTTP-DELETE request, the corresponding Action methods Put, Post, and Delete will be filtered out.
Filter query strings and route Variables
Some parameters bound to the target Action method (the parameter type supports string type conversion) value comes from the request URL, specifically. For the former, the value is generally converted into the HttpRouteData generated by the routing system when the channel variable is added. The third round of filtering is performed on the query string of the requested URL.
An Action method can be normally executed on the premise that the parameter value of the method can be properly bound. The caller must provide the parameter values required for executing the target method in the request. For many candidate actions, if the parameter value is provided by the query string of the request URL or the HttpRouteData is generated, the Action that can be selected for processing a request must meet the following conditions :.
Taking our defined DemoController as an example, if you receive a HTTP-GET request with a URL of ', for five actions that support HTTP-GET, only the first three Action methods (Get () the parameters of Retrieve () and Get (string x) can be provided through the URL query string, so they will be selected. For the other two Action methods (Get (string x, string y) and Get (int x, int y), their parameter y cannot be obtained from the request, therefore, it is excluded from the selected range.
We will discuss how to filter query strings and route variables in ApiControllerActionSelector. For each candidate Action, ApiControllerActionSelector stores a set of parameter names through a string array. The value bound to the corresponding parameter comes from the request URL. Specifically, these parameters must meet the following three conditions:
- The parameter should not be a default parameter, because the default parameter has a default value, and the corresponding value does not necessarily exist in the request URL.
- The parameter type must support conversion from the string type, because the parameter value appears in the URL in the form of a string.
- The parameter corresponds to HttpParameterBinding to read data from the current URI.
For the five actions obtained from the previous filtering, according to the preceding three conditions, this array of parameter names is used to save the parameter values that must be provided by URL, as shown in the left figure. To facilitate subsequent instructions, we name this array.
When this round of filtering starts, ApiControllerActionSelector generates HttpRouteData to get all the route variable names, then, remove the variable "controller" that represents the name of the target HttpController and the variable "Action" that represents the name of the target action (if any). We will get the string set named ParameterNames1. It then extracts all query string names from the request URL. Assume that the string set is named ParameterNames2. Finally, ParameterNames1 and ParameterNames2 are merged to obtain a new string set ParameterNames3.
Based on the principle that "the request must provide the parameter values required for the target Action method", for a large number of candidate actions, only their UrlParameters are subsets of ParameterNames3 will be selected. But can all actions that meet this condition become the remaining members after this round of filtering?
For example, if the URL of the current request (HTTP-GET) is '/api/demo? X = 1 ", so the first three actions meet the preceding conditions. Does it mean that all three actions will be selected? If the request URL is changed to "/api/demo? X = 1 & y = 2 ". No doubt all five actions meet the preceding conditions. Will all these five actions be selected for this round of filtering?
In our experience, when we call 3rd actions (Get (string x), we will use "/api/demo? X = 1 ". If the request URL is"/api/demo? X = 1 & y = 2 ". Generally, the last two actions (Get (string x, string y) and Get (int x, int y) are accessed )). In fact, ApiControllerActionSelector is also the final choice: For many qualified candidate actions, it only retains the group of actions with the most UrlParameters elements.
The above section describes how to filter actions when the request URL has a query string or the generated HttpRouteData contains the corresponding routing variables (excluding the routing variables indicating the target HttpController and Action name. If the URL does not contain any query string at all, and the generated HttpRouteData does not contain any routing variables, the Action filtering becomes simple: only the Action with UrlParameters null is selected.
Based on the above two conditions, the Action filtering policy is applicable to URLs ("/api/demo", "/api/demo? X = 1 ","/api/demo? X = 1 & y = 2 ") for the HTTP-GET request, the list of the remaining five candidate actions that are retained after this round of filtering is shown on the right.
Filter ActionMethodSelector
Based on the previous introduction, we know that ActionMethodSelector is used to determine whether a specific request target Action is valid. The last round of filtering is performed for the ActionMethodSelector applied to the Action method. The specific filtering logic is simple: the IActionMethodSelector interface is directly obtained from the application to the candidate Action method, And the MethodInfo corresponding to the current HttpControllerContext and Action method is used as the parameter to call its IsValidForRequest method ,.
We also know that IActionMethodSelector is an internal interface type. Currently, there is only one unique NoActionAttribute to implement this interface, so we only need to pay attention to it. This round of filtering removes the Action that has applied the NoActionAttribute feature.
For this example, only the NoActionAttribute feature is applied to the Retrieve method, so if it is included in the list of candidate actions, it will be removed. The URLs are "/api/demo" and "/api/demo? X = 1 ","/api/demo? X = 1 & y = 2 "for the three HTTP-GET requests, the final reserved Action list is shown in 5-9.
Exception Handling
ASP. NET Web API selects the default Action policy for the request, that is, the implementation logic of the SelectAction method defined in ApiControllerActionSelector will go through four rounds of filtering. If the selected result only has a unique Action, it will be used to process the current request. In addition, there are two exceptions:
- No Action is selected
- Multiple matched actions
For the former, ASP. NET Web API directly returns a response in the "404, Not Found" status. The latter throws an InvalidOperationException because it cannot determine which Action is used to process the current request. For our example, if the URL of the received HTTP-GET request is/api/demo,/api/demo? X = 1 ", because only the unique Action is selected, the request can be processed normally. As shown in the figure on the right, we can directly obtain the expected results by visiting two addresses in the browser.
If the request URL is "/api/demo? X = 1 & y = 2 ". We know that there will be two Action Methods matching the request in this case, so such a request cannot be processed normally. As shown in the left figure, when we access this address, the browser directly displays the XML containing the error message. The specific error message is "Multiple actions were found that match the request: System. string Get (System. string, System. string) on type WebApi. controllers. demoController System. string Get (Int32, Int32) on type WebApi. controllers. demoController ", that is," multiple actions that match the current request are defined in the target HttpController ".
Now we make the following changes to DemoController and apply the NoActionAttribute feature directly to the Get method without parameters or parameters.
DemoController : ApiController
{
[NoAction]
Get()
{
;
}
}
Following the Action selection policy described above, if it is currently a HTTP-GET request with a URL of "/api/demo", no matching Action is found in the target HttpController, the client will receive a response in the "404, Not Found" status. Access this address through a browser, as shown in the right figure.
How does an ASP. NET Web API select an Action based on the request? [Part 1]
How does an ASP. NET Web API select an Action based on the request? [Part II]