Asp.Net Web API 2第八课——Web API 2中的属性路由

简介: 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html   路由就是Web API如何把URI匹配到一个Action的描述。

前言

阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html

  路由就是Web API如何把URI匹配到一个Action的描述。Web API支持一种新的路由类型,被叫做属性路由。顾名思义,属性路由是用属性来创建路由。在你的Web API中属性路由可以让你更好的控制URI。你能容易的创建描述资源阶层的URIs。

  较早的基于公约的路由风格是全面被支持的。事实上,你能够在同一个项目中联合使用这两种技术。

  本文主要展示如何启用属性路由,并且描述了属性路由的各种选项,内容如下:

  1、为什么使用属性路由?

  2、启用属性路由

  3、添加路由属性

  4、路由前缀

  5、路由约束

  6、可选的URI参数和默认值

  7、路由名称

  8、路由顺序

1、为什么使用属性路由

  第一个Web API版本使用的是基于公约的路由。在该类型的路由中, 你可以定义一个或者多个被参数化字符串的模版。当这个框架接收到一个请求时,它匹配一个URI到路由模版。有关基于公约的路由的详细介绍可以参考之前的文章:http://www.cnblogs.com/aehyok/p/3442051.html

  基于公约的路由的一个优势就是,这个模版被定义在一个单独的地方。这个路由规则一致的被应用于所有的控制器。不幸的是,基于公约的路由是很难支持确切的URI模式,而这个确切的URI模式在Restful APIs中是很普遍的。例如,资源经常包含子资源:客户下了订单,电影有演员,书有作者等等,它是很自然的创建这些URI来反应这些关系:

/customers/1/orders

这种类型的URI在基于公约的路由下是比较难实现的。尽管它能做到,但是如果你有许多控制器或者很多资源类型这种结果不能很好的被扩展。

对于属性路由,它是很容易的为这个URI定义一个路由。你可以简单的添加一个属性到控制器的动作上:

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

这有一些属性路由使它更容易的其他URI模式。

API版本控制

 在下面的例子中,"api/v1/products"相对于"api/v2/products"将被路由到不同的控制器。

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

重载URI片段

在下面的例子中,"1"是一个阶数,而"pending"被映射到集合。

/orders/1
/orders/pending

多个参数类型

在下面的例子中,"1"是一个阶数,而“2013/06/16”被指定为一个日期。

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

 

2、启用属性路由

 要启用属性路由,在配置期间需要调用MapHttpAttributeRoutes。这个扩展方法被定义在System.Web.Http.HttpConfigurationExtensions类中。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
    }
}

你也可以将属性路由与基于公约的路由一起使用。为了定义基于公约的路由,需要调用MapHttpRoute 的方法。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

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

3、添加路由属性

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

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

这个[Route]属性定义了一个HTTP Get方法。这个字符串“customers/{customerId}/orders”是路由的URI模版。在路由模版中的“{customerId}”参数匹配了在方法中的customerId参数的名称。 例如,这个路由将匹配如下的URI:

http://example.com/customers/1/orders

这个URI模版可以有多个参数:

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

任何没有路由属性的控制器方法将使用基于公约的路由。这种方式,你可以结合两种方式在同一个项目中。

4、路由前缀

 通常情况下,在同一个控制器中的所有路由以相同的前缀开头。例如:

public class BooksController : ApiController
{
    [Route("api/books")]
    public IEnumerable<Book> GetBooks() { ... }

    [Route("api/books/{id:int}")]
    public Book GetBook(int id) { ... }

    [Route("api/books")]
    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) { ... }
}

在方法属性上可以用一个波浪符号重写路由前缀:

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

5、路由约束

 路由约束可以让你在路由模版中限制参数被匹配。常规的语法是"{parameter:constraint}",例如:

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

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

如果URI的“id”片段是一个integer类型的,那么第一个路由将会被选择,否则第二个路由将会被选择。

下面是被支持的约束列表:

注意到一些限制,例如"min",带参数在括号里。您可以应用多个约束的参数,用冒号分隔。

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

 自定义路由约束

你能够创建自定义的路由约束通过实现这个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) { ... }

你也能替换整个DefaultInlineConstraintResolver 类通过实现IInlineConstraintResolver 接口。这样做将替换所有的内置约束。除非在IInlineConstraintResolver 的实现特地的添加它们。

6、可选的URI参数和默认值

 你可以通过添加一个问号标记路由参数使成为一个可选的URI参数。如果一个路由参数是可选的,你必须为这个方法参数定义一个默认值。

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

在这个例子中,"/api/books/locale/1033"和"/api/books/locale"将返回同样的资源。

或者,你能在路由模版中定义一个默认值,如下:

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

这个示例几乎和上面的例子一样。但是当默认值被应用的时候,行为上有一个轻微的不同。

1、在第一个例子中("{lcid?}"),1033默认值被直接指定在方法参数上。因此这个参数将有一个精确的值。

2、在第二个例子中("{lcid?}"),1033默认值通过模型绑定进程。这个默认的模型绑定将转换“1033”为1033数值。但是,你可以插入自定义模型绑定,其中可能做不同的事情。

在大多数情况下,除非在你的管道中有自定义的模型绑定,这两种表现是等价的。

7、路由名称

 在Web API中,每个路由都有一个名称。路由名称被用于生成链接,你能在HTTP响应中包含一个链接。

指定这个路由名称,在这个属性上设置RouteName属性。下面的例子展示怎样设置路由名称,当生成一个链接也能用这个路由名称。

public class BooksController : ApiController
{
    [Route("api/books/{id}", RouteName="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;
    }
}

如果你不设置RouteName 属性,Web API产生这个名字。这个默认的路由名称是"ControllerName.ActionName"。在前面的例子中,对于这个GetBook 方法这个默认的路由名称将是“Books.GetBook”。对于同一个动作名称如果控制器有多重的属性路由,一个后缀将被添加。例如,“Books.GetBook1" 和 "Books.GetBook2"。

8、路由顺序

   当一个框架试图讲一个URI匹配到路由的时候,它会在特定的顺序下评估这些路由。为了指定这个顺序,在路由属性上设置RouteOrder 属性。较低的值将首先被评估。这默认的顺序值是0。

这里是如何确定的总排序:

1.比较路由属性中的RouteName 属性。

2.在路由模版中查看每个URI片段。对于每个片段,顺序如下:

  •   文本片段。
  •   带有约束的路由参数。
  •   不带有约束的路由参数。
  •   带有约束的通配符路由参数。
  •   不带有约束的通配符路由参数。

3.In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.(这句话还没搞太明白

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

[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

注意"details"是一个文本片段出现在"{id}"之前,但是"pending"将出现在最后因为这个RouteOrder 属性是1。(这个例子假定没有客户命名为"details"或者"pending")。总之,试图去避免模糊不清的路由。在这个例子中,GetByCustomer 的一个较好的路由模版是"customers/{customerName}")。

总结

 属性路由,很不错,随心所欲,想怎么定义很方便,真是一大亮点吧。

本文参考链接http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

本文同时也已经更新至系列导航中http://www.cnblogs.com/aehyok/p/3446289.html 

目录
相关文章
|
12天前
|
前端开发 搜索推荐 开发者
揭秘Web前端的隐形英雄:神奇的title与alt属性,你真的了解吗?
【8月更文挑战第26天】在Web开发中,`title`和`alt`属性对于提升网站的可访问性和搜索引擎优化至关重要。`title`属性可在鼠标悬停时显示额外信息,增强用户体验;`alt`属性主要用于图像,提供替代文本以确保视觉障碍用户及搜索引擎能理解图像内容。正确使用这两个属性可以显著提高网站的友好性和可达性。
26 0
|
7天前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
21 1
|
17天前
|
监控 前端开发 Serverless
现代化 Web 应用构建问题之观测站点的PV、UV和API异常等指标如何解决
现代化 Web 应用构建问题之观测站点的PV、UV和API异常等指标如何解决
26 2
|
23天前
|
XML 开发框架 .NET
ASP.NET Web Api 如何使用 Swagger 管理 API
ASP.NET Web Api 如何使用 Swagger 管理 API
|
7天前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
29 0
|
7天前
|
API Java Python
API的神秘面纱:从零开始构建你的RESTful服务
【8月更文挑战第31天】在现代网络应用开发中,RESTful API已成为数据交互的标准。本文通过比较流行的技术栈(如Node.js、Python的Django和Flask、Java的Spring Boot)及其框架,帮助你理解构建RESTful API的关键差异,涵盖性能、可扩展性、开发效率、社区支持、安全性和维护性等方面,并提供示例代码和最佳实践,指导你选择最适合项目需求的工具,构建高效、安全且易维护的API服务。
16 0
|
7天前
|
Java 开发者 关系型数据库
JSF与AWS的神秘之旅:如何在云端部署JSF应用,让你的Web应用如虎添翼?
【8月更文挑战第31天】在云计算蓬勃发展的今天,AWS已成为企业级应用的首选平台。本文探讨了在AWS上部署JSF(JavaServer Faces)应用的方法,这是一种广泛使用的Java Web框架。通过了解并利用AWS的基础设施与服务,如EC2、RDS 和 S3,开发者能够高效地部署和管理JSF应用。文章还提供了具体的部署步骤示例,并讨论了使用AWS可能遇到的挑战及应对策略,帮助开发者更好地利用AWS的强大功能,提升Web应用开发效率。
31 0
|
7天前
|
API 网络安全 数据库
Web2py框架如何颠覆传统的RESTful API开发?掌握这些技巧,让你的开发效率飞跃!
【8月更文挑战第31天】Web2py是一款全栈Python Web框架,适用于快速开发复杂交互的Web应用。本文将介绍如何使用Web2py创建RESTful API,包括设置新控制器、定义RESTful路由、处理数据库交互、确保API安全性、编写文档与使用Swagger、测试API以及部署时的注意事项。Web2py的高度抽象和易用性使其成为实现RESTful API的理想选择,帮助开发者专注于业务逻辑而非技术细节。
13 0
|
10天前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
23 0
|
14天前
|
移动开发 数据挖掘 API
HTML5 中 Web Workers API 的用法
【8月更文挑战第24天】
31 0
下一篇
DDNS