[Web API series tutorial] 2.2-routing and Action Selection Mechanism in ASP. NET Web API

Source: Internet
Author: User

[Web API series tutorial] 2.2-routing and Action Selection Mechanism in ASP. NET Web API

This article describes how ASP. NET Web APIs route HTTP requests to specific actions on the controller.

Note: For a high-level overview of Routing, see Routing in ASP. NET Web API.

This article focuses on the details of the routing process. If you create a Web API project and find that some requests do not get the corresponding routes as expected, I hope this article will help you.

There are three main phases of routing:

Match the URI to the routing template, select a controller, and select an action.

You can replace some of these processes with your habits. In this article, I will describe the default behavior. At the end, I will point out where you can customize the behavior.

Route Templates)

The routing template looks very similar to the URI path, but it can contain placeholders specified in braces.

"api/{controller}/public/{category}/{id}"

When you create a route, you provide the default values for some or all placeholders:

defaults: new { category = "all" }

You can also provide some constraints (constraints), which limit how the URI field can match a placeholder:

constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.

The framework will try its best to match the fields in the URI path to the template. The text in the template must be exactly matched. A placeholder can match multiple variables unless you specify constraints. The framework does not match other parts of the URI, such as the host name or query parameter. The framework selects the first route in the routing table used to match the URI.

There are two special placeholders: "{controller}" and "{action }".

"{Controller}" provides the controller name. "{Action}" provides the action name. In Web APIs, "{action}" is ignored }". Ults

If you provide the default API, the route matches the URI that is missing. For example:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}",
    defaults: new { category = "all" }
);

For URIHttp: // localhost/api/productsThe route is matched.{Category}The field will be assigned the default valueAll.

Route Dictionary)

If the framework finds a URI match, it creates a dictionary set that contains the values applicable to each placeholder. A key is a placeholder name that does not contain braces. The value is extracted from the URI path or the default form. The dictionary is stored in the IHttpRouteData object.

In the routing matching stage, placeholders "{controller}" and "{action}" are treated like other placeholders. They are simply stored in the dictionary together with other values.

For ults, it can have a special value RouteParameter. Optional. If a placeholder is assigned to this value, this value is not added to the routing dictionary. For example:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{category}/{id}",
    defaults: new { category = "all", id = RouteParameter.Optional }
);

For the URI path "api/products", the routing dictionary will include:

Controller: "products" category: "all"

However, for "api/products/toys/123", the routing dictionary will include:

Controller: "products" category: "all" id: "123"

For ults, it also contains a value that does not appear anywhere in the routing template. If the route matches, the value is stored in the dictionary. For example:

routes.MapHttpRoute(
    name: "Root",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "customers", id = RouteParameter.Optional }
);

If the URI path is "api/root/8", the dictionary contains two values:

Controller: "customers" id: "8" Selecting a Controller)

The Controller selection is handled by the IHttpControllerSelector. SelectController method. This method requires an HttpRequestMessage instance to be passed in and an HttpControllerDescriptor object to be returned. The default implementation is implemented by the DefaultHttpControllerSelector class. This class uses a simple algorithm:

In the routing dictionary, find the key "controller". Extract the value corresponding to this key and add the string "Controller" to get the type name of the Controller. Use this type name to find a Web API Controller.

For example, if the routing dictionary contains key-value pairs "controller" = "products", the controller type is "ProductsController ". If there are no matching types or multiple matching types, the framework will send an error to the client.

For step 3, DefaultHttpControllerSelector uses the IHttpControllerTypeResolver interface to obtain a list of Web API controller types. The default Implementation of IHttpControllerTypeResolver will return (a) Implementation of IHttpController, (B) not abstract, (c) All public classes whose names end with "Controller.

Action Selection

After the controller is selected, the Framework calls the IHttpActionSelector. SelectAction method to select the action. In this method, you need to input an HttpControllerContext parameter and return an HttpActionDescriptor object.

The default implementation is provided by the ApiControllerActionSelector class. To select an action, it will search for it as follows:
1) Request HTTP Method
2) Placeholder "{action}" in the routing template (if any)
3) parameters of actions in the Controller

Before viewing the selection algorithm, we need to understand something about the Controller action.

Which methods in the controller are considered as "actions"?When selecting an action, the framework only looks for common instance methods in the controller. Of course, it will exclude some "special" methods (constructors, events, Operation overloading, etc.) and methods inherited from the ApiController class.

HTTP method.The framework only selects the action that matches the HTTP Method of the request. It depends on the following:

You can use an attribute to specify the HTTP Method: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, or HttpPut. Alternatively, if the name of a controller method starts with "Get", "Post", "Put", "Delete", "Head", "Options", or "Patch, the HTTP method is supported as agreed. If the preceding values are not included, the POST method is supported.

Parameter binding.Parameter binding refers to how a Web API creates a value for a parameter. Here is the default rule for parameter binding:

Simple types extract complex types directly from Uris and extract request Weights

Simple types include all. NET Framework basic types (. NET Framework primitive types), plus DateTime, Decimal, Guid, String, and TimeSpan. For each action, a maximum of one parameter can be used to read the Request body.

Note: it is also possible to reload the default binding rules. View WebAPI Parameter binding under the hood.

With the above background knowledge, here is the algorithm for selecting actions:

Create an action list for the Controller that matches the HTTP request method. The action routing dictionary contains "action" records and removes actions whose names do not match this value.

According to the following rules, try to match the action parameters to the URI:

A gets a simple list of parameters for each action when the binding retrieves parameters from the URI. Run the optional parameters. From this list, B tries its best to find matching for each parameter name in the routing dictionary or URI query string. Matching is case insensitive and does not depend on the parameter order. C. When each parameter in the list has a match in the URI, select an action. D. If multiple actions comply with these criteria, select one of them with the maximum number of parameter matches.

Ignore actions that contain the [NonAction] attribute.

Step 3 may be the most confusing. The basic idea is that a parameter can obtain its value from URI, request body, or binding. For parameters from the URI, we will ensure that the URI does contain a value for the parameter, whether in the path (through the routing dictionary) or in the query string.

For example, consider the following actions:

public void Get(int id)

This id parameter is bound to the URI. Therefore, this action can match a URI containing the value "id", whether in the routing dictionary or query string.

Optional parameters are an exception because they are optional. For optional parameters, it does not matter if the value cannot be obtained from the URI.

For some different reasons, complex types are also an exception. Complex types can only be bound to Uris through custom binding. However, in this case, the framework cannot know in advance that the parameter may be bound to a special URI. To understand it, you need to execute this binding. The goal of this algorithm is to select an action from the static description before any binding is executed. Therefore, complex types are executed from this matching algorithm.

After the action is selected, All Parameter Bindings are executed.

Summary:

The action must match the HTTP Method of the request. The action name (if any) must match all the parameters of the action in the "action" entry in the routing dictionary. If the parameter is extracted from the URI, the parameter name must be found in the routing dictionary or URI query string. (Except for optional and complex parameters .) Try to match the maximum number of parameters. However, the best match may be a method that does not contain any parameters. Extended Example)

Route:

routes.MapHttpRoute(
    name: "ApiRoot",
    routeTemplate: "api/root/{id}",
    defaults: new { controller = "products", id = RouteParameter.Optional }
);
routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Controller:

public class ProductsController : ApiController
{
    public IEnumerableGetAll() {}
    public Product GetById(int id, double version = 1.0) {}
    [HttpGet]
    public void FindProductsByName(string name) {}
    public void Post(Product value) {}
    public void Put(int id, Product value) {}
}

HTTP request:

GET http://localhost:34701/api/products/1?version=1.5&details=1
Route match)

The URI matches the Route named "DefaultApi. This routing dictionary contains the following entries:

Controller: "products" id: "1"

This routing dictionary does not contain the query strings "version" and "details", but these are still considered during the action selection.

Controller Selection)

According to the "controller" entry in the routing dictionary, the controller type is ProductsController.

Action Selection)

This HTTP request is a GET request. The GetAll, GetById, and FindProductsByName controllers that support GET are supported. The routing dictionary does not contain any "action" entries, so we do not need to match the action name.

Next, we try to match the parameter name of the action, and now we only search for it in the GET action.

Action Parameters to Match
GetAll None
GetById "Id"
FindProductsByName "Name"

Note that the version parameter of GetById is not considered because it is an optional parameter.

Obviously, the GetAll method can match, and the GetById method can also match, because the routing dictionary contains "id". The FindProductsByName method does not match.

Finally, the GetById method wins because it can match a parameter. Correspondingly, no parameter can match GetAll. This method is executed along with the values of the following parameters:

Id = 1 version = 1.5

Note that although the version parameter is not used in the selection algorithm, the value of this parameter is still from the URI query string.

Extension Points)

Web APIs Provide extensions for some parts of the routing process.

Interface Description
IHttpControllerSelector Selects the controller.
IHttpControllerTypeResolver Gets the list of controller types. The DefaultHttpControllerSelector chooses the controller type from this list.
IAssembliesResolver Gets the list of project assemblies. The IHttpControllerTypeResolverinterface uses this list to find the controller types.
IHttpControllerActivator Creates new controller instances.
IHttpActionSelector Selects the action.
IHttpActionInvoker Invokes the action.

To provide your own implementation for any of these interfaces, use the Services set on the HttpConfiguration object:

var config = GlobalConfiguration.Configuration;config.Services.Replace(typeof(IHttpControllerSelector), new MyControllerSelector(config));


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.