前言

阅读本文之前,您也可以到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//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/
/orders/pending

多个参数类型

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

/orders/
/orders///

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 != ;
} string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (Int64.TryParse(valueString, NumberStyles.Integer,
CultureInfo.InvariantCulture, out longValue))
{
return longValue != ;
}
}
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 = ) { ... }
}

在这个例子中,"/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 = )]
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

Asp.Net Web API 2第八课——Web API 2中的属性路由的更多相关文章

  1. ASP.NET Web API 2 中的属性路由使用(转载)

    转载地址:ASP.NET Web API 2 中的属性路由使用

  2. Web API 2中的属性路由

    Web API 2中的属性路由 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.ht ...

  3. Asp.Net Web API 2第六课——Web API路由和动作选择

    Asp.Net Web API 导航 Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web AP ...

  4. Asp.Net Web API 2第五课——Web API路由

    Asp.Net Web API 导航   Asp.Net Web API第一课——入门 http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web ...

  5. Asp.Net Web API 2第七课——Web API异常处理

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

  6. ASP.NET Web API 2中的属性路由(Attribute Routing)

    如何启用属性路由并描述属性路由的各种选项? Why Attribute Routing? Web API的第一个版本使用基于约定的路由.在这种类型的路由中,您可以定义一个或多个路由模板,这些模板基本上 ...

  7. 潭州课堂25班:Ph201805201 WEB 之 Ajax第八课 (课堂笔记)

    js <——>jq <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  8. 十八、React react-router4.x中:实现路由模块化、以及嵌套路由父子组件传值

    一.路由模块化(用字典定义路由,然后循环出来) 1.官方文档参考 [官方文档]https://reacttraining.com/react-router/web/guides/quick-start ...

  9. Web API中的路由(二)——属性路由

    一.属性路由的概念 路由让webapi将一个uri匹配到对应的action,Web API 2支持一种新类型的路由:属性路由.顾名思义,属性路由使用属性来定义路由.通过属性路由,我们可以更好地控制We ...

随机推荐

  1. Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO

    本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO   1.  多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...

  2. 一致性hash介绍

    像Memcache以及其它一些内存K/V数据库一样,Redis本身不提供分布式支持,所以在部署多台Redis服务器时,就需要解决如何把数据分散到各个服务器的问题,并且在服务器数量变化时,能做到最大程度 ...

  3. mongo 查找附近点

    db.runCommand({geoNear:"demo", near: { type: "Point" , coordinates: [118.134535, ...

  4. Docker私有仓库 Registry中的镜像管理

    这里主要介绍Registry v2的版本 查看Registry仓库中现有的镜像: # curl -XGET http://10.0.30.6:5000/v2/_catalog# curl -XGET ...

  5. 关于sql注入

    删除表,先猜表名,可以使用下面的语名: Select * from A where A.a = ‘testdata’; drop table A---’; If a field only allow ...

  6. phpunit.xml

    <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name=&quo ...

  7. 了解 XSS 攻击原理

    在了解 XSS 之前必须知道『网站登入(Session)』的原理 简单的说当会员成功登入后 网站会给浏览器一个『令牌』 之后只要拿着这个『令牌』到网站上 就会被视为已经登入 再来下面是 XSS 最简单 ...

  8. ssh 互通脚本

    实现了 主机到指定机器的ssh免密码登录. 若要实现互通, 则在机器列表的每台机器上执行该脚本. 192.168.1.22 root test 192.168.1.25 root test 192.1 ...

  9. Nop源码分析一

    从Global.asax文件开始逐层分析Nop的架构. Application_Start()方法作为mvc启动的第一个方法. 1,首先初始化一个引擎上下文,如下面的代码: EngineContext ...

  10. 点亮一个led灯

    /********************************* 代码功能:点亮一个led灯 使用函数: pinMode(引脚号,模式); digitalWrite(引脚号,电平状态); //默认 ...