[Web API series tutorial] 2.3-attribute routing in ASP. NET Web API 2

Source: Internet
Author: User

[Web API series tutorial] 2.3-attribute routing in ASP. NET Web API 2
Preface

Routing refers to how a Web API matches a specific action. Web API 2 supports a new routing type, called attribute routing. As its name suggests, property routes use properties to define routes. Attribute routing gives you more control over the web api uri. For example, you can easily create a URI that describes hierarchical resources.

The earlier routing style was known as the agreed-based routing and is still fully supported. You can use these two technologies in the same project.

This topic describes how to enable a property route and describes various options of the property route. For more information about how to use Attribute Routing, see Create a rest api with Attribute Routing in Web API 2.

? Why Attribute Routing?
? Enabling Attribute Routing
? Adding Route Attributes
? Route Prefixes
? Route Constraints
? Optional URI Parameters and Default Values
? Route Names
? Route Order

Prerequisites (Prerequisites)

Visual Studio 2013 or Visual Studio Express 2013

Alternatively, use NuGet Package Manager to install necessary packages. Under the Tools directory of Visual Studio, select Library Package Manager, and then select Package Manager Console. Enter the following command in the Package Manager Console:

Install-Package Microsoft.AspNet.WebApi.WebHost
Why Attribute Routing?

The first release of Web API uses a convention-based route. In that route, you define one or more routing templates, which are basic parameter strings. When the framework receives a request, it matches the URI to the routing template. (For more information about Routing based on conventions, see Routing in ASP. NET Web API)

One advantage of the agreed routing is that the template is defined in a single place, and the routing rules will be applied to all controllers. Unfortunately, it is difficult to support a URI mode that is common in RESTful APIs based on the agreed route. For example, resources usually include sub-resources: customers include orders, movies include actors, books include authors, and so on. Therefore, you can naturally create Uris that map these relationships:
/Mers MERs/1/orders
With attribute routing, you can easily define a route for this URI. You only need to simply add an attribute to the Controller action:

[Route("customers/{customerId}/orders")]public IEnumerable
  
    GetOrdersByCustomer(int customerId) { ... }
  

Here, some other modes become easier because of the property routing:

API versioning

In this example, "api/v1/products" and "api/v2/products" may be routed to different controllers.
/Api/v1/products
/Api/v2/products

Overloaded URI segments

In this example, "1" is an order number, but "pending" is mapped to a set.
/Orders/1
/Orders/pending

Multiple parameter types

In this example, "1" is an order number, but "" is a date.
/Orders/1
/Orders/2013/06/10

Enabling Attribute Routing)

To enable attribute routing, you need to call MapHttpAttributeRoutes during configuration. This extension method is defined in the System. Web. Http. HttpConfigurationExtensions class.

using System.Web.Http;namespace WebApplication{    public static class WebApiConfig    {        public static void Register(HttpConfiguration config)        {            // Web API routes            config.MapHttpAttributeRoutes();            // Other Web API configuration not shown.        }    }}

Attribute routing can also be combined with a specified route. To define a route based on the Conventions, call the MapHttpRoute method.

public static class WebApiConfig{    public static void Register(HttpConfiguration config)    {        // Attribute routing.        config.MapHttpAttributeRoutes();        // Convention-based routing.        config.Routes.MapHttpRoute(            name: "DefaultApi",            routeTemplate: "api/{controller}/{id}",            defaults: new { id = RouteParameter.Optional }        );    }}

For more information about Configuring Web APIs, see processing ing ASP. NET Web API 2.

Before Web API 2, the code generated by the Web API project target is as follows:

protected void Application_Start(){    // WARNING - Not compatible with attribute routing.    WebApiConfig.Register(GlobalConfiguration.Configuration);}

If the property route is not enabled, this code throws an exception. If you upgrade an existing Web API project to use Attribute routing, make sure that the configuration code is upgraded as follows:

protected void Application_Start(){    // Pass a delegate to the Configure method.    GlobalConfiguration.Configure(WebApiConfig.Register);}

Note: For more information, see routing ing Web API with ASP. NET Hosting.

Adding Route Attributes)

Here is an example of using properties to define a route:

public class OrdersController : ApiController{    [Route("customers/{customerId}/orders")]    [HttpGet]    public IEnumerable
  
    FindOrdersByCustomer(int customerId) { ... }}
  

The string "MERs/{customerId}/orders" is a URI template for routing. The Web API will try its best to match the requested URI to the template. In this example, "MERs" and "orders" are both literal fields, and "{customerId}" is a variable parameter. The following Uris match the template:

1,  http://localhost/customers/1/orders2,  http://localhost/customers/bob/orders3,  http://localhost/customer/1234-5678/orders

You can use constraints to restrict these matches, which will be described later in this topic.

Note that the "{customerId}" parameter of the routing template matches the customerId parameter name in the method. When the Web API executes the Controller action, it tries its best to bind route parameters. For example, when the URI is http: // example.com/customers/1/orders, the Web API tries its best to bind the value "1" to the customerId parameter in the action.

A URI template can have multiple parameters:

[Route("customers/{customerId}/orders/{orderId}")]public Order GetOrderByCustomer(int customerId, int orderId) { ... }

Any Controller method without routing attributes uses a specified route. On this basis, you can use both routing types in the same project.

HTTP Methods

Web APIs also select actions based on HTTP requests (such as GET and POST. By default, the Web API searches for matching based on the Controller method name without case sensitivity. For example, a controller method named PutCustomers matches an http put request.

You can also add these attributes to the method to reload the rule:

? [HttpDelete]
? [HttpGet]
? [HttpHead]
? [HttpOptions]
? [HttpPatch]
? [HttpPost]
? [HttpPut]

The following example maps the CreateBook Method to the http post request.

[Route("api/books")][HttpPost]public HttpResponseMessage CreateBook(Book book) { ... }

For all HTTP methods, including non-standard methods, you can use the AcceptVerbs attribute. It requires a list of HTTP methods.

// WebDAV method[Route("api/books")][AcceptVerbs("MKCOL")]public void MakeCollection() { }
Route Prefixes)

Generally, the routes in the Controller start with the same prefix. For example:

public class BooksController : ApiController{    [Route("api/books")]    public IEnumerable
  
    GetBooks() { ... }    [Route("api/books/{id:int}")]    public Book GetBook(int id) { ... }    [Route("api/books")]    [HttpPost]    public HttpResponseMessage CreateBook(Book book) { ... }}
  

You can use the [RoutePrefix] attribute to set a public prefix for the entire controller.

[RoutePrefix("api/books")]public class BooksController : ApiController{    // GET api/books    [Route("")]    public IEnumerable
  
    Get() { ... }    // GET api/books/5    [Route("{id:int}")]    public Book Get(int id) { ... }    // POST api/books    [Route("")]    public HttpResponseMessage Post(Book book) { ... }}
  

Use a wildcard (~) on the method property (~) To overload the routing prefix.

[RoutePrefix("api/books")]public class BooksController : ApiController{    // GET /api/authors/1/books    [Route("~/api/authors/{authorId:int}/books")]    public IEnumerable
  
    GetByAuthor(int authorId) { ... }    // ...}
  

The route prefix can also contain parameters:

[RoutePrefix("customers/{customerId}")]public class OrdersController : ApiController{    // GET customers/1/orders    [Route("orders")]    public IEnumerable
  
    Get(int customerId) { ... }}
  
Route Constraints)

Routing constraints allow you to restrict how parameters in the routing template are matched. The general syntax is "{parameter: constraint }". For example:

[Route("users/{id:int}"]public User GetUserById(int id) { ... }[Route("users/{name}"]public User GetUserByName(string name) { ... }

Here, the first route is selected only when the URI's "id" field is an integer. Otherwise, the second route is selected.

The following table lists the supported constraints.

Constraint Description Example
Alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {X: alpha}
Bool Matches a Boolean value. {X: bool}
Datetime Matches a DateTime value. {X: datetime}
Decimal Matches a decimal value. {X: decimal}
Double Matches a 64-bit floating-point value. {X: double}
Float Matches a 32-bit floating-point value. {X: float}
Guid Matches a GUID value. {X: guid}
Int Matches a 32-bit integer value. {X: int}
Length Matches a string with the specified length or within a specified range of lengths. {X: length (6)} {x: length (1, 20 )}
Long Matches a 64-bit integer value. {X: long}
Max Matches an integer with a maximum value. {X: max (10 )}
Maxlength Matches a string with a maximum length. {X: maxlength (10 )}
Min Matches an integer with a minimum value. {X: min (10 )}
Minlength Matches a string with a minimum length. {X: minlength (10 )}
Range Matches an integer within a range of values. {X: range (10, 50 )}
Regex Matches a regular expression. {X: regex (^ \ d {3}-\ d {3}-\ d {4} $ )}

Note that some of the constraints still need parameters in parentheses, such as "min ". You can apply multiple constraints to one parameter, which are separated by colons.

[Route("users/{id:int:min(1)}")]public User GetUserById(int id) { ... }
Custom Route Constraints (Custom Route Constraints)

You can create a custom route constraint by implementing the IHttpRouteConstraint interface. For example, the following constraints limit a parameter to a non-zero integer value.

public class NonZeroConstraint : IHttpRouteConstraint{    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,         IDictionary
  
    values, HttpRouteDirection routeDirection)    {        object value;        if (values.TryGetValue(parameterName, out value) && value != null)        {            long longValue;            if (value is long)            {                longValue = (long)value;                return longValue != 0;            }            string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);            if (Int64.TryParse(valueString, NumberStyles.Integer,                 CultureInfo.InvariantCulture, out longValue))            {                return longValue != 0;            }        }        return false;    }}
  

The following code demonstrates how to register constraints:

public static class WebApiConfig{    public static void Register(HttpConfiguration config)    {        var constraintResolver = new DefaultInlineConstraintResolver();        constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));        config.MapHttpAttributeRoutes(constraintResolver);    }}

Now you can apply the constraint to your route:

[Route("{id:nonzero}")]public HttpResponseMessage GetNonZero(int id) { ... }

You can also replace the entire DefaultInlineConstraintResolver class by implementing the IInlineConstraintResolver interface. This will replace all the built-in constraints unless the IInlineConstraintResolver you implement specifically adds them.

Optional URI parameters and default values

You can add the hello mark to the route parameter to make a URI parameter optional. If a route parameter is optional, you must define the default value for the method parameter.

public class BooksController : ApiController{    [Route("api/books/locale/{lcid:int?}")]    public IEnumerable
  
    GetBooksByLocale(int lcid = 1033) { ... }}
  

In this example,/api/books/locale/1033 and/api/books/locale will return the same resource.

Alternatively, you can specify a default value in the routing template, as shown below:

public class BooksController : ApiController{    [Route("api/books/locale/{lcid:int=1033}")]    public IEnumerable
  
    GetBooksByLocale(int lcid) { ... }}
  

This is roughly the same as the previous example, but there are nuances when the default value is applied.
1. In the first example ("{Icid ?}"), The default value 1033 is directly assigned to the method parameter, so the parameter will have an accurate value.
2. In the second example ("{Icid = 1033}"), the default value 1033 will be through the model binding process. The default model binding will convert 1033 to a numeric value of 1033. However, you may encounter a custom model binding, which may cause errors.
(In most cases, unless you have custom model bindings in your pipeline, the two forms are equivalent .)

Route name (Route Names)

In Web APIs, each route has a name. The route name is very useful for generating links, so you can include a link in the HTTP response.

To specify the route Name, set the Name attribute on the attribute ). The following example shows how to select a route name and how to use the route name when a link is generated.

public class BooksController : ApiController{    [Route("api/books/{id}", Name="GetBookById")]    public BookDto GetBook(int id)     {        // Implementation not shown...    }    [Route("api/books")]    public HttpResponseMessage Post(Book book)    {        // Validate and add book to database (not shown)        var response = Request.CreateResponse(HttpStatusCode.Created);        // Generate a link to the new book and set the Location header in the response.        string uri = Url.Link("GetBookById", new { id = book.BookId });        response.Headers.Location = new Uri(uri);        return response;    }}
Route Order)

When the framework tries to match the URI with a route, it will get a specific route order. To specify the order, set the RouteOrder attribute on the route attribute. The lower-case value comes first, and the default order value is zero.

The following describes how to determine the sequence:
1. Compare the RouteOrder attribute of each route attribute
2. Search for each URI field in the routing template. For each field, the order is determined by the following factors:
-Literal Field
-Route parameters with Constraints
-Route parameters that do not contain constraints
-Wildcard parameter fields containing Constraints
-Wildcard parameter fields that do not contain constraints
3. In the case of a tie, the routing order is determined by comparing the original strings of the routing template that are case-insensitive.

This is an example. Assume that you define the following controller:

[RoutePrefix("orders")]public class OrdersController : ApiController{    [Route("{id:int}")] // constrained parameter    public HttpResponseMessage Get(int id) { ... }    [Route("details")]  // literal    public HttpResponseMessage GetDetails() { ... }    [Route("pending", RouteOrder = 1)]    public HttpResponseMessage GetPending() { ... }    [Route("{customerName}")]  // unconstrained parameter    public HttpResponseMessage GetByCustomer(string customerName) { ... }    [Route("{*date:datetime}")]  // wildcard    public HttpResponseMessage Get(DateTime date) { ... }}

The routing sequence is as follows:

Orders/details orders/{id} orders/{customerName} orders/{* date} orders/pending

Note that "details" is a literal field and appears before "{id}", while "pending" appears at the end because its RouteOrder is 1. (This example assumes that no customer is named "details" or "pending ". Generally, try to avoid ambiguous routes. In this example, a better routing template for GetByCustomer is "MERs/{customerName }".)

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.