一、路由介绍

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

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

二、两种路由模式

2.1 模板路由

模板路由是ASP.NET Web 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文件夹下。

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

1.我们添加一个测试控制器api。

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

用fiddldr调试如下:

2.我们添加两个方法如下:

    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.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属性来确定的。

惯例是:

1、静态片段变量
2、带约束的片段变量
3、不带约束的片段变量
4、带约束的通配符片段变量
5、不带约束的通配符片段变量

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

ASP.NET WebApi 路由配置【转】的更多相关文章

  1. ASP.NET WebApi 路由配置

    ASP.NET Web API路由是整个API的入口.我们访问某个资源就是通过路由映射找到对应资源的URL.通过URL来获取资源的. 对于ASP.NET Web API内部实现来讲,我们的请求最终将定 ...

  2. 史上最全的ASP.NET MVC路由配置

    MVC将一个Web应用分解为:Model.View和Controller.ASP.NET MVC框架提供了一个可以代替ASP.NETWebForm的基于MVC设计模式的应用. AD:51CTO 网+ ...

  3. ASP.NET MVC路由配置(转载自http://www.cnblogs.com/zeusro/p/RouteConfig.html )

    把apress.pro.asp.net.mvc.4.framework里的CHAPTER 13翻译过来罢了. XD 首先说URL的构造. 其实这个也谈不上构造,只是语法特性吧. 命名参数规范+匿名对象 ...

  4. 史上最全的ASP.NET MVC路由配置,以后RouteConfig再弄不懂神仙都难救你啦~

    继续延续坑爹标题系列.其实只是把apress.pro.asp.net.mvc.4.framework里的CHAPTER 13翻译过来罢了,当做自己总结吧.内容看看就好,排版就不要吐槽了,反正我知道你也 ...

  5. Asp.Net Webapi路由基本设置

    1.直接在Global.asax中添加配置 如: using MvcApplication4.App_Start; using System; using System.Collections.Gen ...

  6. asp.net MVC路由配置总结

    URL构造 命名参数规范+匿名对象 routes.MapRoute(name: "Default",url: "{controller}/{action}/{id}&qu ...

  7. ASP.NET MVC路由配置

    一.命名参数规范+匿名对象 routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}" ...

  8. (转)ASP.NET MVC路由配置

    一.命名参数规范+匿名对象 1 routes.MapRoute(name: "Default", 2 url: "{controller}/{action}/{id}&q ...

  9. ASP.NET WebAPI 路由规则与POST数据 【转】

    http://www.cnblogs.com/liulun/archive/2012/06/20/2556556.html 蛋疼的路由规则约定 在上一篇文章中 我们成功通过AJAX获取到了服务器的数据 ...

随机推荐

  1. 开源的报表系统easyreport的部署

    https://github.com/xianrendzw/EasyReport/blob/master/docs/manual/version2_0.md

  2. linux ssh_config和sshd_config配置文件

    在远程管理linux系统基本上都要使用到ssh,原因很简单:telnet.FTP等传输方式是‍以明文传送用户认证信息,本质上是不安全的,存在被网络窃听的危险.SSH(Secure Shell)目前较可 ...

  3. android Handlerr.removeCallbacksAndMessages(null)的妙用

    今天在阅读代码发现了android  Handlerr.removeCallbacksAndMessages(null)代码 在ondestory()调用,之前没用过,那想弄懂咋办,查api: pub ...

  4. 山寨一个std::bind\boost::bind

    这里是最初始的版本,参考https://github.com/cplusplus-study/fork_stl/blob/master/include/bind.hpp 提供了最简洁的实现方式. 第一 ...

  5. Cocos2d-x CCScale9Sprite 用法

    1.创建方式有三种: (1).直接创建 auto blocks = Scale9Sprite::create("blocks9.png", Rect(0, 0, 96, 96), ...

  6. ubuntu文件夹默认列表显示

    编辑-->首选项-->视图-->列表视图

  7. Makefile初探

    选择一个目录创建一个Makefile文件: 注意第二行的开头需要时TAB建空开,不要用空格 执行make make的时候,无论你创建的是makefile还是Makefile都可以识别 ,不在乎开头的字 ...

  8. [转]Windows Server 2008 对 CPU 及 RAM 的支持规格

    Windows Server 2008 对 CPU 的支援: 在看到下表时,请注意其数字所指的是:主板上的实体 CPU的个数,也就是几个 Sockets 举例来说,机器上安装 2 个 4 核心的 CP ...

  9. VBA学习笔记(8)--遍历所有文件夹和文件

    说明(2017.3.26): 1. 采用的是兰色幻想教学视频中的“父子转换法” 2. 这种VBA的遍历文件夹方法非常难理解,主要是因为dir这个函数,第一次带参数调用,返回的是此目录下的第一个文件,第 ...

  10. idea中maven依赖不能下载的解决办法

    使用maven 命令 maven install 在项目所在文件夹 执行.