ASP.NET WebApi 路由配置

1、路由介绍

ASP.NET Web API路由是整个API的入口。咱们访问某个资源就是经过路由映射找到对应资源的URL。经过URL来获取资源的。html

对于ASP.NET Web API内部实现来说,咱们的请求最终将定位到一个具体的Action上。因此说,ASP.NET Web API路由就是把客户端请求映射到对应的Action上的过程。web

 

2、两种路由模式

2.1 模板路由

 

模板路由是ASP.NET Web API默认提供的路由。下面咱们就简单讲解此中路由的用法。正则表达式

 

默认模板路由api

模板路由使用前须要定义路由模板。以下面默认的路由模板:测试

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http; namespace Supernova.Webapi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }

 

此模板路由是新建项目默认生成的,在App_Start文件夹下。ui

 

咱们能够看到此模板的URL格式是api/{controller}/{id}。api表明在资源前面要带上api目录,controller表明请求资源的控制器名称。id表明一条资源的id,id 是可选的。这种默认的模板是不带action的,因此它是以请求方式来区分资源的,咱们必须在action上添加请求方式特性加以区分。spa

1.咱们添加一个测试控制器api。设计

 


public class TestController : ApiController { public object Get1() { return "d1"; } }

用fiddldr调试以下:调试

 

 

2.咱们添加两个方法以下:htm

 


public class TestController : ApiController { public object Get1() { return "d1"; } public object Get2() { return "d2"; } }

 

 

咱们再用fiddler调试以下:

 

错误信息是:

{"Message":"出现错误。","ExceptionMessage":"找到了与该请求匹配的多个操做: \r\n类型 Supernova.Webapi.Controllers.TestController 的 Get1\r\n类型 Supernova.Webapi.Controllers.TestController 的 Get2","ExceptionType":"System.InvalidOperationException","StackTrace":"   在 System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n   在 System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n   在 System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}

 

咱们将代码改成以下:

 


public class TestController : ApiController { public object Get1() { return "d1"; } [HttpPost] public object Get2() { return "d2"; } }

调试返回Get1的信息。

 

 

从上面两个测试咱们能够得出以下结论:

 

  • action的默认请求方式是HttpGet。
  • 当多个action的 请求方式同样的话,在默认路由模板下(没有action),将会匹配多个操做。
  • 基于上面两点结论,默认路由模板没法知足针对一种资源一种请求方式的多种操做(好比修改操做,可能针对不一样的字段进行修改)。

 

 

定制模板路由

咱们从新定制模板路由,以下:

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http; namespace Supernova.Webapi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }

从上面咱们能够看出,在默认路由的基础上,咱们队路由模板增长了一级action。

 

测试api以下:

 


public class TestController : ApiController { public object Get1() { return "d1"; } public object Get2() { return "d2"; } }

咱们再经过http://192.168.0.230/api/test访问,返回404,如图:

 

 

咱们经过http://192.168.0.230/api/test/Get1访问,结果正确,如图:

 

咱们经过http://192.168.0.230/api/test/Get2访问,结果正确,如图:

 

 

经过定制路由模板咱们能够得出以下结论:

 

  • 经过在路由模板中增长action目录,对资源的定位直接做用到action上。
  • 多个HttpGet方法能够共存于一个controller中。
  • 基于上面两点结论,经过修改路由模板能够知足针对一种资源一种请求方式的多种操做。

 

 

 

 

2.2 特性路由

特性路由是经过给action打attribute的方式定义路由规则。

有时候咱们会有这样的需求,咱们请求的一个资源带有子资源。好比文章评论这样有关联关系的资源。咱们但愿经过以下URL得到某篇文章下的全部评论:api/book/id/comments。而仅仅凭借模板路由很难实现这种路由模式。这时候咱们就须要特性路由来解决这个问题了。ASP.NET Web API为咱们准备了Route特性,该特性能够直接打到Action上,使用很是灵活、直观。

下面我将先简单的介绍特性路由的使用方法。

咱们从新定义api以下:

 


public class TestController : ApiController { [Route("demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }

咱们能够看出,在action打上了标签。

 

使用fiddler调试以下:

请求Get1的URL是http://192.168.0.230/demo

 

请求Get2的URL是http://192.168.0.230/demo/get

 

2.3 两种路由的对比

  1. 模板路由针对于简单的业务很是方便,可是对于复杂的资源操做略显余力不足。
  2. 特性路由能够在不更改action名称的状况下灵活修改。
  3. 特性路由对于关联资源能够建立友好的URL。
  4. 特性路由能够对于路由参数的约束,起到精细化控制。

 

 

3、特性路由详解

3.1 使用Route特性

使用特性路由很简单,不须要作额外的配置,只须要在action上打上Route标签就能够了。这样模板路由就自动失效了。

以下:

 


public class TestController : ApiController { [Route("demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }

 

3.2 使用RoutePrefix特性

有时候咱们想对某个资源的全部操做都加上一个统一的前缀。

第一种方式:

 


public class TestController : ApiController { [Route("api/demo")] [HttpGet] public object Get1() { return "d1"; } [Route("api/demo/get")] [HttpGet] public object Get2() { return "d2"; } }

这种方式看起来还能够哈,就是有点弱智。那么咱们就可使用RoutePrefix将特性加在controller上面,那么对资源的请求就要加上api目录了。

 

第二种方法:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }

3.3 重写Action的前缀规则

3.2中的方法能够对某个资源的前面统一加上 前缀。那么问题来了,若是咱们还会有这样的需求,个人某个资源中的大部分请求都须要前缀,可是就是有那么一两个资源不须要加前缀,肿么办?其实微软早就给咱们想到了,人家说了,固然容许你重写action前缀啊。

以下代码,咱们从新了Get1:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("~/demo")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/get")] [HttpGet] public object Get2() { return "d2"; } }

用fiddler调试以下:

 

 

报错了吧,正确的URL实际上是:http://192.168.0.230/demo

3.4.1  路由参数约束

如今问题又来了,那么多的请求,特别是Get请求方式,都须要带参数啊,怎么定义参数的类型,长度范围等约束条件呢?

答案是能够经过"{参数变量名称:约束}"来约束路由中的参数变量。

ASP.NET Web API内置约束包括:
{x:alpha} 约束大小写英文字母
{x:bool}
{x:datetime}
{x:decimal}
{x:double}
{x:float}
{x:guid}
{x:int}
{x:length(6)}
{x:length(1,20)} 约束长度范围
{x:long}
{x:maxlength(10)}
{x:min(10)}
{x:range(10,50)}
{x:regex(正则表达式)}

以下代码:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int}")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }

 

以上,若是片断变量id为int类型,就路由到第一个Action Get1,若是不是,路由到第二个Action Get2。

使用fiddler调试以下:

请求是Get1.

 

请求的是Get2

 

能够为一个参数变量同时设置多个约束:

 

以下代码:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int:min(5)}")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }

请求URL:http://192.168.0.230/api/demo/1 定位到Get2

 

3.4.2 扩展路由参数约束

实现IHttpRouteConstraint接口,可自定义约束规则。实现一个不能为0的约束。

代码以下:

 


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

 

在App_Start文件夹中的WebApiConfig中注册自定义约束。必需要注释原先的模板路由

 

 


public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 //config.MapHttpAttributeRoutes(); //config.Routes.MapHttpRoute( // name: "DefaultApi", // routeTemplate: "api/{controller}/{action}/{id}", // defaults: new { id = RouteParameter.Optional } //); var constraintResolver = new DefaultInlineConstraintResolver(); constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint)); config.MapHttpAttributeRoutes(constraintResolver); } }

测试代码以下:

 

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:nonzero}")] [HttpGet] public object Get1() { return "d1"; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }

使用URL:http://192.168.0.230/api/demo/0 定位到Get2

3.5 可选参数及其默认值

有时候,咱们请求的参数是可选的,怎么办呢,咱们就须要给参数设置默认值来处理了。

代码以下:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int?}")] [HttpGet] public object Get1(int id=1) { return "d1"+id; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }

当参数不存在或者为int类型时定位的是Get1,当参数存在不为int时定位的是Get2.

 

URL:http://192.168.0.230/api/demo 定位 Get1
URL:http://192.168.0.230/api/demo/2 定位 Get1
URL:http://192.168.0.230/api/demo/abc 定位 Get2

3.6 给路由设置名称

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("demo/{id:int?}",Name="经过ID获取内容")] [HttpGet] public object Get1(int id = 1) { return "d1" + id; } [Route("demo/{name}")] [HttpGet] public object Get2() { return "d2"; } }

 

3.7 路由优先顺序

 

Route特性设置的路由优先顺序是根据惯例和RouteOrder属性来肯定的。

 

惯例是:

一、静态片断变量
二、带约束的片断变量
三、不带约束的片断变量
四、带约束的通配符片断变量
五、不带约束的通配符片断变量 

RouteOrder属性的默认值是0,属性值越小,排在越前面。

 

 

测试代码以下,按照优先级来的:

 


[RoutePrefix("api")] public class TestController : ApiController { [Route("orders/detail", Name = "静态片断变量")] [HttpGet] public object Get1() { return "orders/detail"; } [Route("orders/{id:int}", Name = "带约束的片断变量")] [HttpGet] public object Get2(int id) { return "orders/{id:int}"; } [Route("orders/{name}", Name = "不带约束的片断变量")] [HttpGet] public object Get3(string name) { return "orders/{name}"; } [Route("orders/lily", Order = 1)] [HttpGet] public object Get4() { return "orders/lily"; } }

 

 

URL:http://192.168.0.230/api/orders/detail 定位 Get1    静态片断变量 Order=0

URL:http://192.168.0.230/api/orders/lily定位 Get3       带约束的片断变量 Order=0

URL:http://192.168.0.230/api/orders/1定位 Get2      不约束的片断变量 Order=0

 

Get3包含了Get4的定义,因此说永远也没法定义到Get4。这也是在特性路由中须要特别注意的地方。

 

3.8 路由设计规范

1.URL中不能出现动词。 

参考:

http://www.eggtwo.com/news/detail/155

https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

http://www.cnblogs.com/n-pei/archive/2012/07/17/2595352.html

相关文章
相关标签/搜索