zoukankan      html  css  js  c++  java
  • ASP.NET Web API 2中的属性路由(Attribute Routing)

    如何启用属性路由并描述属性路由的各种选项?

    Why Attribute Routing?

    Web API的第一个版本使用基于约定的路由。在这种类型的路由中,您可以定义一个或多个路由模板,这些模板基本上是参数化字符串。当框架收到请求时,它会将URI与路由模板进行匹配。

    基于约定的路由的一个优点是模板在单个位置定义,并且路由规则在所有控制器上一致地应用。遗憾的是,基于约定的路由使得很难支持RESTful API中常见的某些URI模式。例如,资源通常包含子资源:客户有订单,电影有演员,书有作者,等等。创建反映这些关系的URI是很自然的:

    /customers/1/orders

    使用基于约定的路由很难创建这种类型的URI。尽管可以这样做,但如果您有许多控制器或资源类型,结果将无法很好地扩展。

    使用属性路由,为此URI定义路由很简单。您只需向控制器操作添加一个属性:

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

    启用属性路由:Enabling Attribute Routing

    要启用属性路由,请在WebApiConfig配置文件调用MapHttpAttributeRoutes此扩展方法在System.Web.Http.HttpConfigurationExtensions类中定义

    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.
            }
        }
    }

    属性路由可以与基于约定的路由组合。要定义基于约定的路由,请调用MapHttpRoute方法:

    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 }
            );
        }
    }

    注意:从Web API 1迁移

    在Web API 2之前,Web API项目模板生成如下代码:

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

    如果启用了属性路由,则此代码将引发异常。如果升级现有Web API项目以使用属性路由,请确保将此配置代码更新为以下内容:

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

    添加路由属性:Adding Route Attributes

    以下是使用属性定义的路径示例:

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

    字符串“customers / {customerId} / orders”是路径的URI模板。Web API尝试将请求URI与模板匹配。在此示例中,“customers”和“orders”是文字段,“{customerId}”是可变参数。以下URI将与此模板匹配:

    • http://localhost/customers/1/orders
    • http://localhost/customers/bob/orders
    • http://localhost/customers/1234-5678/orders

    请注意,路由模板中的“{customerId}”参数与方法中customerId参数的名称相匹配。

    当Web API调用控制器操作时,它会尝试绑定路由参数。例如,如果URI为http://example.com/customers/1/orders,则Web API会尝试将值“1”绑定到操作中的customerId参数。

    任何没有路由属性的控制器方法都使用基于约定的路由。这样,您可以在同一个项目中组合两种类型的路由。

    HTTP方法

    Web API还根据请求的HTTP方法(GET,POST等)选择操作。默认情况下,Web API会查找与控制器方法名称的开头不区分大小写的匹配项。例如,控制器方法名:PutCustomers匹配 HTTP PUT请求。

    您可以通过使用以下任何属性修饰方法来覆盖此约定:

    • [HttpDelete]
    • [HTTPGET]
    • [HttpHead]
    • [HttpOptions]
    • [HttpPatch]
    • [HttpPost]
    • [HttpPut]

    以下示例将CreateBook方法映射到HTTP POST请求。

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

    对于所有其他HTTP方法(包括非标准方法),请使用AcceptVerbs属性,该属性采用HTTP方法列表

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

    路由前缀:Route Prefixes

    通常,控制器中的路由都以相同的前缀开头。例如:

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

    您可以使用[RoutePrefix]属性为整个控制器设置公共前缀

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

    在method属性上使用波浪号(〜)来覆盖路由前缀:

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

    路由前缀可以包含参数:

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

    路由约束:Route Constraints

    路由约束允许您限制路径模板中的参数匹配方式。一般语法是“{parameter:constraint}”。例如:

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

    下表列出了支持的约束:

    ConstraintDescriptionExample
    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}$)}

    请注意,某些约束(例如“min”)在括号中包含参数。您可以将多个约束应用于参数,以冒号分隔。

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

    自定义路由约束:Custom Route Constraints

    您可以通过实现IHttpRouteConstraint接口来创建自定义路由约束例如,以下约束将参数限制为非零整数值。
     
    public class NonZeroConstraint : IHttpRouteConstraint
    {
        public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
            IDictionary<string, object> 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;
        }
    }

    以下代码显示了如何注册约束:

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

    现在您可以在路由中应用约束:

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

    您还可以通过实现IInlineConstraintResolver接口替换整个DefaultInlineConstraintResolver这样做将替换所有内置约束,除非您的IInlineConstraintResolver实现专门添加它们。

    可选的URI参数和默认值

    您可以通过向路由参数添加问号来使URI参数可选。如果route参数是可选的,则必须为method参数定义默认值。

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

    在这个例子中,/api/books/locale/1033/api/books/locale返回相同的资源。

    或者,您可以在路径模板中指定默认值,如下所示:

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

    路由名称:Route Names

    在Web API中,每个路由都有一个名称。路由名称对于生成链接非常有用,因此您可以在HTTP响应中包含链接。

    要指定路由名称,请在属性上设置Name属性。以下示例显示如何设置路由名称,以及如何在生成链接时使用路由名称。

    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

    当框架尝试将URI与路由匹配时,它会按特定顺序评估路由。要指定顺序,请在route属性上设置RouteOrder属性。首先评估较低的值。默认排序值为零。

    以下是确定总排序的方式:

    1. 比较route属性RouteOrder属性。

    2. 查看路由模板中的每个URI段。对于每个细分,顺序如下:

      1. 文字片段。
      2. 使用约束路由参数。
      3. 路由参数没有约束。
      4. 具有约束的通配符参数段。
      5. 没有约束的通配符参数段。
    3. In the case of a tie,,路由按路由模板的不区分大小写的序列比较(OrdinalIgnoreCase排序

    [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) { ... }
    }

    这些路由按如下顺序排列.

    1. orders/details
    2. orders/{id}
    3. orders/{customerName}
    4. orders/{*date}
    5. orders/pending
  • 相关阅读:
    洛谷—— P2234 [HNOI2002]营业额统计
    BZOJ——3555: [Ctsc2014]企鹅QQ
    CodeVs——T 4919 线段树练习4
    python(35)- 异常处理
    August 29th 2016 Week 36th Monday
    August 28th 2016 Week 36th Sunday
    August 27th 2016 Week 35th Saturday
    August 26th 2016 Week 35th Friday
    August 25th 2016 Week 35th Thursday
    August 24th 2016 Week 35th Wednesday
  • 原文地址:https://www.cnblogs.com/AndyChen2015/p/9592015.html
Copyright © 2011-2022 走看看