特性路由 是Web API 2 中提出的一种新的类型的路由,正如其名称那样,它是通过特性(Attribute) 来定义路由的,相比之前的基于模式(Convertion Based)的路由,特性路由 能够提供更为灵活和更多的控制。更好的方式是,灵活的组合使用这两种方式。

为什么需要特性路由

  在 特性路由 之前 的 基于模式 的路由,我们需要定义一些包含一些参数化字符串的模板,例如,api/{congroller}/{action}/{id},当接受到请求后,会将请求的 URI 与这些模板进行匹配,这种方式有一个优点那就是所有的路由定义都可以在同一个地方进行配置,这些规则将被应用到所有的 Controller,这也造成也其对一些特定的 URI 的匹配不够灵活,例如,请求的一些资源(Resource) 包含一些子资源,例如顾客具有订单,电影包含演员,书籍具有作者等,这时自然而然的会创建如下的 URI 来映射这种关系:/customers/1/orders。此时如果使用 基于模式 的方式来定义路由规则便会极为的困难,即使能够处理,在具有较多 Controller 和 资源类型时,也并不能达到很好的效果。

  使用特性路由 就可以很好的解决这样的问题,像上面的例子,使用 特性路由 可以很方便的定义满足条件的路由规则,如下所示:

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

特性路由的使用

启用特性路由

  为了启用特性路由,需要调用 System.Web.Http.HttpConfigurationExtensions 的扩展方法 MapHttpAttributeRoutes 进行配置。在 App_Start 目录的 WebApiConfig 类中进行配置。

     public static void Register(HttpConfiguration config)
{
// Web API 路由
config.MapHttpAttributeRoutes(); //Convention-based Route
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

使用特性路由

  下面有一些使用 特性路由 的技巧

  1. 指定 API 版本,下面的两个路由规则分别匹配不同版本的 API 方法
  • api/v1/products
  • api/v2/products
  1. 重载 URI 片段
        /// <summary>
/// 根据商品名称获取商品
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[Route("product/{name}")]
public IHttpActionResult GetProduct(string name)
{
if (string.IsNullOrEmpty(name))
return Ok(products); var product = products.Where(p =>
{
if (p.Name == name)
return true;
return false;
}).FirstOrDefault(); if(product != null){
return Ok(product);
}
else
{
return NotFound();
}
}
/// <summary>
/// 返回商品列表
/// </summary>
/// <returns></returns>
[Route("product")]
public IHttpActionResult GetProduct()
{
return Ok(products);
}
  1. 使用多种类型的参数
		/// <summary>
/// 根据商品Id获取商品信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Route("api/product/{id:int}")]
public IHttpActionResult GetProduct(int id)
{ } /// <summary>
/// 根据上市时间获取商品
/// </summary>
/// <param name="marketdate">上市时间</param>
/// <returns></returns>
[Route("api/product/{*marketdate:datetime}")]
public IHttpActionResult GetProduct(DateTime marketdate)
{ }
路由前缀

  仔细观察,上面配置的特性路由都具有相同的前缀 api/product/, 使用路由前缀(Route Prefix)我们可以在 Controller 进行统一设置,例如前面的例子,我们就可以改为下面的样子:

	[RoutePrefix("api/product")]
public class DefaultController : ApiController {
[Route("{id:int}")]
public IHttpActionResult GetProduct(int id){
}
}

  我们还可以使用 ~ 符号对使用了路由前缀的 Controller 中的方法进行路由重写,例如:

    [RoutePrefix("api/product")]
public class DefaultController : ApiController {
[Route("~/api/discount/product/{id:int}")]
public IHttpActionResult GetProduct(int id){
}

  上面的例子使用 ~ 对路由进行了重写,新的路由规则将覆盖 Controller 定义的路由前缀,现在该方法所匹配的规则为 api/discount/product/{id:int},而非原来的 api/product/{id:int}

  在定义路由前缀时,还可以包含一些参数,例如:

	[RoutePrefix(api/{customerid})]
public class DefaultController : ApiController{
[Route("order")]
public IEnumerable<Order> GetOrders(int customerid){
}
}
可选路由参数和参数默认值

  我们可以使用 ? 将一个路由参数标记为 可选参数(optional parameter),如果一个路由参数被标记为可选的,则必须为其设置默认值。

	[Route("api/product/{id:int?}")]
public IHttpActionResult GetProduct(int id =1){
}

  此时,对于 /api/product/id/1/api/product 将返回相同的结果。

  我们不仅可以在方法中设置参数的默认值,还可以在路由特性中进行设置。如下所示,

	[Route("api/product/{id:int=1}")]
public IHttpActionResult GetProduct(int id){
}

  上面设置默认值的方法基本完全相同,但是在应用该值时还是有细微的区别,如下所示:

  

  • 对于第一种方式,默认值 1 是直接分配给方法的参数的,因此其总是具有确定的值
  • 对于第二种方式,默认值通过了 模型绑定(Model- Binding) 的过程,在绑定过程中,它会从 "1" 被转换为 System.Int32 类型的 1,然而,我们可以定义自己的模型绑定器,因此,最终参数 id 的默认值并不是确定的,因为其在自定义的模型绑定器中可能被转换为其它的结果。

  通常情况下,使用两种形式给予参数默认值的形式的结果是相同的。

路由参数约束(Constraint)

  通过路由参数约束我们可以将路由模板中的参数限制为指定的类型,通用的语法如下所示:{parameter:costraint},例如前面的例子,将 id的类型限制为 System.Int32

[Route("api/product/{id:int}")]
public IHttpActionResult GetProduct(int id){} [Route("api/product/name")]
public IHttpActionResult GetProduct(string name){}

  只有请求的参数为 Int 类型时才会匹配第一个路由规则,否则匹配第二个。

  下表罗列了可用的约束,

  

约束 描述 例子
alpha 将参数约束为大写或者小写的拉丁字母(a-z,A-Z) {x:alpha}
bool 将参数限制为 bool 类型 {x:bool}
datetime 将参数限制为 date {x:date}
decimal 将参数限制为 decimal 类型 {x:decimal}
float 将参数限制为 32 位浮点数类型 {x:float}
double 将参数限制为 64 位浮点数类型 {x:double}
int 将参数限制为 32 整形 {x:int}
long 将参数限制为 64位整形 {x:long}
guid 将参数类型限制为 guid 类型 {x:guid}
length 将参数的长度限制为指定长度或指定范围的长度 {x:length(5)/{x:length(1,10)}
min/max 限制参数(整形)最大或最小值 {x:min(10)}/{x:max(10)}
minlength/maxlength 限制参数的最小长度或最大长度 {x:minlength(1)}/{x:maxlength}
range 限制参数(整形) 的范围,包含两端 {x:range(1,3)}
regex 限制参数必须匹配指定的正则表达式 {x:regex(\expression)}

  可以同时使用多个约束条件,每个条件之间通过 进行分割,例如

	[Route("api/product/{id:int:min(1)}")]
public IHttpActionResult GetProduct(int id){}

  除了 内置的约束条件,我们还可以定制自己的约束条件,方法是实现 IHttpConstraint 接口,重写其 public bool Match(   HttpRequestMessage request,   IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection ) 方法

然后通过如下的方式注册自定义的约束后,便可像使用内置约束那样的使用自定义约束了

	public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add(identity, typeof(CustomConstrainType)); config.MapHttpAttributeRoutes(constraintResolver);
}
}
Http 方法

  Web API 在选择 Action 时还会基于请求的 Http 方法进行判断, Web Api 默认会匹配控制器方法的开端(匹配不区分大小写),例如,GetProduct 便会被识别为一个 Http Get 方法。我们可以使用内置的一些 Http Method 特性来覆盖默认的实现。

  • HttGet
  • HttpPost
  • HttpHead
  • HttpOptions
  • HttpDelete
  • HttpPacth
  • HttpPut

下面的方法,使用 HttpPost 特性将其标记为 POST 方法

[HttpPost]
[Route("api/product/{id}")]
public IHttpActionResult CreateProduct(){}

  除此之外,还可以使用AcceptVerbs 特性来实现,其接受一个上述特性的列表(方法名字符串列表)

路由名称

  在 Web Api 中每个路由都有自己的名称,这个名称在产生链接是十分的有用。

	[Route("{id:int}",Name ="GetProductById")]
public Product Get(int id)
{
var product = products.Where(p =>
{
if (p.Id == id)
return true;
return false;
}).FirstOrDefault();
return product;
}
[Route("~/api/products/{id:int}")]
[HttpGet]
public HttpResponseMessage Find(int id)
{
var response = Request.CreateResponse(HttpStatusCode.Created);
string uri = Url.Link("GetProductById", new { id = });///api/product/id //response.Headers.Location = new Uri(uri);
response.Content = new StringContent(uri);
return response;
}
路由的顺序

  当使用一个 Route 去匹配一个 URI 时,会以一个特定的顺序去分析路由,我们可以设置 Route 的 RouteOrder ** 来指定一个路由的顺序,RouteOrder** 是一个整形数字,默认值为0,其值越小,顺序越靠前。

  当接受到一个请求后,会以如下的顺序去匹配路由

  1. 比较路由表中各路由的 RouteOrder
  2. 查看各路由模板中的 URI 片段,对于每个片段按照下面的顺序排列
    1. 字面值(**Literal **)片段
    2. 具有约束条件的路由参数
    3. 没有约束条件的路由参数
    4. 具有约束条件的通配符参数片段
    5. 没有约束条件的通配符参数片段
  3. 最后,这些路由会按照不区分大小写、独立于语言的排序方式进行排序

      总的来说,就是条件越具体的匹配时的顺序越靠前。
	[RoutePrefix("orders")]
public class OrdersController : ApiController
{
[Route("{id:int}")] // 具有约束的参数
public HttpResponseMessage Get(int id) { ... } [Route("details")] // 字面值
public HttpResponseMessage GetDetails() { ... } [Route("pending", RouteOrder = 1)]
public HttpResponseMessage GetPending() { ... } [Route("{customerName}")] // 无约束条件的参数
public HttpResponseMessage GetByCustomer(string customerName) { ... } [Route("{*date:datetime}")] // 通配符
public HttpResponseMessage Get(DateTime date) { ... }

}

 按照上面的规则,可以得出下面的顺序:

  1. orders/details
  2. order/{id:int}
  3. order/{customerName}
  4. order/{*date:datetime}
  5. order/penddig

Web API (四) 特性路由(Attribute Route)的更多相关文章

  1. ASP.NET Web API 中 特性路由(Attribute Routing) 的重名问题

    刚才忘了说了,在控制器名重名的情况下,特性路由是不生效的.不然的话就可以利用特性路由解决同名的问题了. 而且这种不生效是真的不生效,不会提示任何错误,重名或者什么的,直接会报告404,所以也是个坑.

  2. ASP.NET Web API实践系列04,通过Route等特性设置路由

    ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程.在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一 ...

  3. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

  4. Web API中的路由(一)——约定路由

    一.Web API中的路由概念 路由的作用用一句话说明:通过request的uri找到处理该请求的Controller,Action,以及给Action的参数赋值. 一些路由的基本概念: route: ...

  5. Web Api源码(路由注册)

    这篇文章只是我学习Web API框架的输出,学习方法还是输出倒逼输入比较行得通,所以不管写的好不好,坚持下去,肯定有收获.篇幅比较长,仔细思考阅读下来大约需要几分钟. 做.NET开发有好几年时间了,从 ...

  6. ASP.NET Web API中的路由

    ASP.NET Web API的默认路由在App_Start目录中的WebApiConfig.cs文件中定义的. public static class WebApiConfig { public s ...

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

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

  8. WebApi官网学习记录---web api中的路由

    如果一条路由匹配,WebAPI选择controller和action通过如下方式: 1.找到controller,将"controller"赋值给{controller}变量 2. ...

  9. Web API框架学习——路由(一)

    HttpConfiguration(ASP.NET Web API管道的配置是通过HttpConfiguration来完成) : 包括路由注册在内的对整个ASP.NET Web API管道的配置是通过 ...

随机推荐

  1. Spring_Aop的xml和注解的使用

    动态代理:    目的:在不改变源代码的情况下,对方法进行增强!        动态代理又分为两种:    1.第一个就是基于接口的动态代理,他是由jdk提供的    2.基于子类的动态代理:cgli ...

  2. Java Proxy和CGLIB动态代理原理

    动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...

  3. 【Java框架型项目从入门到装逼】第八节 - 用EasyUI绘制主界面

    1.引入资源包 在上一节中,我们把基本的框架都搭好了,用了Spring,SPringMVC.这一节,我们先来画页面,前端框架采用EasyUI来实现. easyui是一种基于jQuery的用户界面插件集 ...

  4. SQLAlchemy基础操作二

    多线程示例 import time import threading from sqlalchemy.ext.declarative import declarative_base from sqla ...

  5. CubeMX使用及感受

    简介 CubeMX这几年刚流行起来,是一个STM32代码的初始化配置工具,里面封装了硬件层.中间层,以及示例代码. cube使用 该软件的安装需要较高版本jdk支持,固件库安装时需要注意和主程序的版本 ...

  6. python语言学习笔记整理

    什么是程序? 程序等于数据结构加算法,那么数据结构是一个静态的东西,算法是一个动态的东西,我们用一个新的语言编写这个程序,我们要考虑到语言也主要由数据结构和算法相关的东西,或静态或动态的东西来构成,所 ...

  7. conda虚拟环境实践

    1.查看本地创建了那些python版本 which python whereis python which主要用来查找可直接执行的命令,可以查找别名.whereis比which的搜索范围大了一些,同时 ...

  8. Jmeter脚本调试之关联----(正则表达式)

    脚本调试 关联,在脚本中,是必应用到的一个设置方法,将脚本中,每次都会动态变化的特殊值进行关联.一个能正确执行的脚本,都需要进行关联(LR.jmeter). Jmeter关联: 在脚本回放过程中,客户 ...

  9. 记录Vue和Jquery混合开发中关于点击事件的一个bug

    最近比较急的接手了公司的微信服务号项目,采用的技术栈主要是jq和vue.在项目中之前碰见过jq写的$().on('click',function(){})点击事件不起作用,只能写在vue实例中的met ...

  10. JavaScript 数组对象的去重

    JavaScript数组去重 1.原型去重法.通过prototype找到数组的源性对象Array,在数组的原型上添加unique()方法.需要使用的时候使用 点 " . " 进行连 ...