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

    网址: https://docs.microsoft.com/zh-cn/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

    ASP.NET Web API 2 中的属性路由

    路由是 Web API 将 URI 与操作匹配的方式。 Web API 2 支持一种称为属性路由的新型路由。 顾名思义,属性路由使用属性来定义路由。 属性路由使您能够对 Web API 中的 URI 进行更多控制。 例如,您可以轻松地创建描述资源层次结构的 URI。

    早期路由样式(称为基于约定的路由)仍完全支持。 事实上,您可以将这两种技术合并到同一项目中。

    本主题演示如何启用属性路由,并介绍属性路由的各种选项。 有关使用属性路由的端到端教程,请参阅在 Web API 2 中使用属性路由创建 REST API。

    先决条件

    视觉工作室 2017社区版、专业版或企业版

    或者,使用 NuGet 包管理器安装必要的包。 从 Visual Studio 中的 "工具"菜单中,选择NuGet 包管理器,然后选择 "包管理器控制台"。 在包管理器控制台窗口中输入以下命令:

    Install-Package Microsoft.AspNet.WebApi.WebHost

    为什么选择属性路由?

    Web API 的第一个版本使用了基于约定的路由。 在这种类型的路由中,定义一个或多个路由模板,这些模板基本上是参数化字符串。 当框架收到请求时,它将 URI 与路由模板匹配。 有关基于约定的路由的详细信息,请参阅ASP.NET Web API 中的路由

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

    /customers/1/orders

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

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

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

    下面是一些其他模式,属性路由使容易。

    API 版本控制

    在此示例中,"/api/v1/产品"将路由到与"/api/v2/产品"不同的控制器。

    /api/v1/products /api/v2/products

    重载 URI 段

    在此示例中,"1"是订单号,但"挂起"映射到集合。

    /orders/1 /orders/pending

    多种参数类型

    在此示例中,"1"是订单号,但"2013/06/16"指定日期。

    /orders/1 /orders/2013/06/16

    启用属性路由

    要启用属性路由,请在配置期间调用MapHttpAttributeRoutes。 此扩展方法在System.Web.http.Http 配置扩展类中定义。

    C#
    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方法。

    C#
    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 的详细信息,请参阅配置ASP.NET Web API 2

    注意:从 Web API 1 迁移

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

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

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

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

     备注

    有关详细信息,请参阅使用托管ASP.NET配置 Web API。

    添加路由属性

    下面是使用属性定义的路由的示例:

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

    字符串"客户/[客户 Id]/订单"是路由的 URI 模板。 Web API 尝试将请求 URI 与模板匹配。 在此示例中,"客户"和"订单"是文本段,"[客户 Id]"是一个变量参数。 以下 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参数。

    URI 模板可以具有多个参数:

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

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

    HTTP 方法

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

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

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

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

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

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

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

    路由前缀

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

    C#
    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] 属性为整个控制器设置公共前缀:

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

    在方法属性上使用波浪线 (*) 来覆盖路由前缀:

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

    路由前缀可以包括参数:

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

    路由约束

    通过路由约束,您可以限制路由模板中的参数的匹配方式。 一般语法是"[参数:约束"] 。 例如:

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

    此处,仅当 URI 的 ID""段是整数时,才会选择第一个路由。 否则,将选择第二个路由。

    下表列出了支持的约束。

    路由约束
    约束说明示例
    alpha 匹配大写或小写拉丁字母字符(a-z,A-Z) [x:阿尔法]
    bool 匹配布尔值。 {x:布尔]
    datetime 匹配日期时间值。 [x:日期时间]
    Decimal 匹配十进制值。 [x:十进制]
    double 匹配 64 位浮点值。 [x:双]
    FLOAT 匹配 32 位浮点值。 [x:浮动]
    guid 匹配 GUID 值。 {x:吉德]
    int 匹配 32 位整数值。 {x:int}
    长度 将字符串与指定长度或指定长度范围内的字符串匹配。 [x:长度(6)][x:长度(1,20)]
    long 匹配 64 位整数值。 [x:长]
    max 匹配具有最大值的整数。 {x:最大(10)]
    最大长度 匹配具有最大长度的字符串。 {x:最大长度(10)]
    min 匹配具有最小值的整数。 {x:min(10)]
    最小长度 匹配长度最小的字符串。 {x:最小长度(10)]
    range 匹配值范围内的整数。 [x:范围(10,50)]
    regex 匹配正则表达式。 {x:regex(d{3}-d{3}-d{4}$)]

    请注意,某些约束(如"最小"值)在括号中采用参数。 您可以将多个约束应用于参数,由冒号分隔。

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

    自定义路由约束

    您可以通过实现IHttpRoute 约束接口来创建自定义路由约束。 例如,以下约束将参数限制为非零整数值。

    C#
    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;
        }
    }
    

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

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

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

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

    您还可以通过实现IInline 约束解析器接口来替换整个默认内联约束解析器类。 这样做将替换所有内置约束,除非您的IInline 约束解析器的实现专门添加它们。

    可选 URI 参数和默认值

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

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

    在此示例中,/api/books/locale/1033/api/books/locale返回相同的资源。

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

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

    这几乎与前一个示例相同,但在应用默认值时,行为有细微差异。

    • 在第一个示例("{lcid:int?}")中,默认值 1033 直接分配给方法参数,因此参数将具有此精确值。
    • 在第二个示例("{lcid:int=1033})中,"1033"的默认值通过模型绑定过程。 默认模型绑定器将"1033"转换为数值 1033。 但是,您可以插入自定义模型活页夹,这可能执行不同操作。

    (在大多数情况下,除非您管道中有自定义模型绑定器,否则这两个窗体将等效。

    路由名称

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

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

    C#
    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;
        }
    }
    

    路线订单

    当框架尝试将 URI 与路由匹配时,它会按特定顺序评估路由。 要指定订单,请在工艺路线属性上设置Order属性。 首先计算较低的值。 默认订单值为零。

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

    1. 比较工艺路线属性的Order属性。

    2. 查看工艺路线模板中的每个 URI 段。 对于每个段,顺序如下:

      1. 字段。
      2. 具有约束的路由参数。
      3. 路由参数没有约束。
      4. 具有约束的通配符参数段。
      5. 通配符参数段没有约束。
    3. 在连接的情况下,路由由路由模板的区分大小写序字符串比较(OrdinalIgnoreCase)排序。

    以下是一个示例。 假设您定义了以下控制器:

    C#
    [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. 订单/详细信息
    2. 订单/{id}
    3. 订单/[客户名称]
    4. 订单/*日期
    5. 订单/待定

    请注意,"详细信息"是文本段,出现在"{id}"之前,但"挂起"显示最后,因为Order属性为 1。 (此示例假定没有名为"详细信息"或"挂起"的客户。 通常,尽量避免不明确的路线。 在此示例中,更好的GetByCustomer路由模板是"客户/[客户名称]"

  • 相关阅读:
    大网管 www.bigit.com
    WinAPI: midiOutUnprepareHeader 清除由 midiOutPrepareHeader 完成的准备
    WinAPI: midiInGetNumDevs 获取 MIDI 输入设备的数目
    WinAPI: midiInUnprepareHeader 清除由 midiInPrepareHeader 完成的准备
    WinAPI: midiInPrepareHeader 为 MIDI 输入准备一个缓冲区
    转贴一组 Delphi 官方网站介绍 IDE 功能的动画
    WinAPI: midiOutPrepareHeader 为 MIDI输出准备一个缓冲区
    WinAPI: midiOutGetNumDevs 获取 MIDI 输出设备的数目
    WinAPI: midiInGetID 获取输入设备 ID
    动画演示 Delphi 2007 IDE 功能[4] 自定义界面
  • 原文地址:https://www.cnblogs.com/zxtceq/p/14789596.html
Copyright © 2011-2022 走看看