为什么要有属性路由

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

/customers/1/orders

使用属性路由,为此URI定义路由很简单,只需向控制器中添加一个属性,如下图所示:

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

启用属性路由

要启用属性路由,要在配置期间调用MapHttpAttributeRoutes,此方法在System.Web.Http.HttpConfigurationExtensions类中定义。如下图所示:

using System.Web.Http;namespace WebApplication
{public static class WebApiConfig{public static void Register(HttpConfiguration config){// Web API routesconfig.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 });}
}

添加属性路由

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相匹配,当WebAPI调用控制器操作时,它会尝试绑定路由参数。例如 URI为 http://example.com/customers/1/orders ,则Web API会尝试将 值“1”绑定到操作的customerId参数中。

URI模板中可以有多个参数,如下图所示:

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

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

以下示例将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() { }

路由前缀

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

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

路径约束允许限制路径模板中的参数匹配方式。一般语法是“{parameter:constraint}”。例如:

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

这时候,只有id是整数时,才匹配第一路径。否则,将选择第二路径。完整的约束列表如下图所示:

约束 描述 例子
alpha 匹配大写或者小写拉丁字母:(a-z , A-Z) {x:alpha}
bool 匹配布尔类型 {x:bool}
datetime 匹配DataTime类型 {x:datetime}
decimal 匹配小数值 {x:decimal}
double 匹配64位浮点数 {x:double}
float 匹配32位浮点数 {x:float}
guid 匹配Guid值 {x:guid}
int 匹配32位整型 {x:int}
length 匹配具有指定长度或者具有指定长度范围的字符串 {x:length(6)} {x:length(1,20)}
long 匹配64位整数值 {x:long}
max 匹配具有最大值的整数 {x:max(10)}
maxlength 匹配具有最大长度的字符串 {x:maxlength(10)}
min 匹配具有最小值的整数 {x:min(10)}
minlength 匹配具有最小长度的字符串 {x:minlength(10)}
range 匹配值范围内的整数 {x:range(10,50)}
regex 匹配正则表达式 {x:regex(^\d{3}-\d{3}-\d{4}$)}

可以将多个约束应用于参数,以冒号分隔

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

可选的URI参数和默认值

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

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

路由名称

在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;}
}

路由顺序

当框架尝试将URI与路由匹配时,它会按特定的顺序评估路由,要指定顺序,需要在Route属性上设置Order属性,框架首先评估Order值较低的方法,默认Order值为零。

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

1、比较Route属性的Order属性。

2、查看路由模板中的每个URI片段,对于细分,约定如下:

a、静态片段。

b、使用约束路由参数。

c、路由参数没有约束。

d、具有约束的通配符参数段

e、没有约束的通配符参数段

[RoutePrefix("orders")]
public class OrdersController : ApiController
{[Route("{id:int}")] // constrained parameterpublic HttpResponseMessage Get(int id) { ... }[Route("details")]  // literalpublic HttpResponseMessage GetDetails() { ... }[Route("pending", RouteOrder = 1)]public HttpResponseMessage GetPending() { ... }[Route("{customerName}")]  // unconstrained parameterpublic HttpResponseMessage GetByCustomer(string customerName) { ... }[Route("{*date:datetime}")]  // wildcardpublic HttpResponseMessage Get(DateTime date) { ... }
}

以上路由将按以下顺序执行:

1、orders/details

2、orders/{id}

3、orders/{customerName}

4、orders/{*date}

5、orders/pending

ASP.NET Web API 中的属性路由相关推荐

  1. ASP.NET Web API中的Controller

    虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...

  2. ASP.NET Web API中实现版本

    一般来说,api 接口是提供给其他系统或是其他公司使用,不能随意频繁的变更.然而,需求和业务不断变化,接口和参数也会发生相应的变化.如果直接对原来的接口进行修改,势必会影响线其他系统的正常运行.这就必 ...

  3. 监控系统简介(二):使用 App Metrics 在 ASP.NET Web API 中记录指标

    回顾 在<监控系统简介:使用 Prometheus 与 Grafana>一文中,我们了解了什么是监控系统,Prometheus 这一监控工具及它提供的数据类型.PromQL 以及 Graf ...

  4. ASP.NET Web API中的参数绑定总结

    ASP.NET Web API中的action参数类型可以分为简单类型和复杂类型. HttpResponseMessage Put(int id, Product item) id是int类型,是简单 ...

  5. 【ASP.NET Web API教程】5.5 ASP.NET Web API中的HTTP Cookie

    5.5 HTTP Cookies in ASP.NET Web API 5.5 ASP.NET Web API中的HTTP Cookie 本文引自:http://www.asp.net/web-api ...

  6. ASP.NET Web API中实现版本的几种方式

    在ASP.NET Web API中,当我们的API发生改变,就涉及到版本问题了.如何实现API的版本呢? 1.通过路由设置版本 最简单的一种方式是通过路由设置,不同的路由,不同的版本,不同的contr ...

  7. 利用查询条件对象,在Asp.net Web API中实现对业务数据的分页查询处理

    在Asp.net Web API中,对业务数据的分页查询处理是一个非常常见的接口,我们需要在查询条件对象中,定义好相应业务的查询参数,排序信息,请求记录数和每页大小信息等内容,根据这些查询信息,我们在 ...

  8. (四)Asp.net web api中的坑-【api的返回值】

    (四)Asp.net web api中的坑-[api的返回值] 原文:(四)Asp.net web api中的坑-[api的返回值] void无返回值 IHttpActionResult HttpRe ...

  9. 在ASP.NET Web API中使用OData的Action和Function

    本篇体验OData的Action和Function功能.上下文信息参考"ASP.NET Web API基于OData的增删改查,以及处理实体间关系".在本文之前,我存在的疑惑包括: ...

最新文章

  1. 即将到来的金三银四,这10道springboot常见面试题你需要了解下
  2. ASP.NET 应用程序生命周期概述
  3. jQuery 常用的效果函数(一)
  4. 摇杆控制方向原理_医用无油空压机的送料作用及工作原理
  5. VSS SVN GIT SVN 加锁签出
  6. ASP语言基础之常量的定义方法
  7. switch安装linux教程,Freeswitch Linux安装教程 | 【韩涛博客】
  8. 加速包可能没用!12306屏蔽多个抢票软件
  9. Python——数据存储:JSON操作
  10. PREV-55 小计算器 (进制转换)
  11. C++ 类使用规范建议
  12. SQLyog下载地址—Mysql的可视化(建议收藏)
  13. javascript边角知识
  14. SoftCnKiller高速下载器捆绑软件杀手
  15. 软件推荐:论文翻译阅读 + 文献管理 + markdown笔记 + 多设备同步 + 一键导出bib参考文献
  16. vant组件做表格_有赞团队的vant ui组件库van-field使用
  17. 联想Lenovo手机平板安装谷歌服务框架Google, Play商店,安装套件GMS
  18. 用Excel绘制曲线图
  19. ThingWorx入门
  20. 线程池ExecutorService

热门文章

  1. python练习笔记——利用信号signal处理僵尸进程
  2. 右击菜单一键优化(增加新建office2003、新建reg和bat,删除新建公文包、新建wps、新建rar)...
  3. 创建laravel项目
  4. [nodejs] 利用openshift 撰寫應用喔
  5. zabbix邮件通知,短信通知配置详解
  6. 多年以后重发:多线程安全的变量模板
  7. asp.net 实现一级域名与二级域名共享COOKIE
  8. 关于C语言野指针的问题
  9. Spring AOP两种使用方式以及如何使用解析
  10. [转载] Java面试题大全(2020版)