• 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
  • 相关阅读:
    通过C#的HttpClient模拟form表单请求
    通过Mysql连接ASP.Net Core2.0(Code First模式)
    每天一点产品思考(1):微博是否可以尝试取消点赞数展示?
    Google Analytics 学习笔记四 —— GA的Channels划分规则
    Google Analytics 学习笔记三 —— GA常用术语
    Google Analytics 学习笔记二 —— GA部署
    Google Analytics 学习笔记一 —— GA简介
    java基础知识总结(一)
    java源码解析之String类(二)
    java源码解析之String类(一)
  • 原文地址:https://www.cnblogs.com/AndyChen2015/p/9592015.html
Copyright © 2020-2023  润新知