ASP.NET Web API实践系列04,通过Route等特性设置路由
ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程。在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一文中,体验了通过模版、惯例、HTTP方法来设置路由,这种做法的好处是把路由模版统一放在了App_Start文件夹下的WebApiConfig类中,方便管理,但缺点是不够灵活。
REST把一切都看成资源,有时候,一个资源连带子资源,比如Customer和Orders密切关联,我们可能希望输入这样的请求:customers/1/orders,但仅仅凭借惯例,很难实现这种路由。而实际上,ASP.NET Web API为我们准备了Route特性,该特性可以直接打到Action上,使用非常灵活、直观。
下面就在ASP.NET MVC4下来体验Route特性的使用方法。
允许Route特性
首先需要在WebApiConfig中设置。
using System.Web.Http;
namespace MyRoutingAttributes4
{
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 }
);
// 取消注释下面的代码行可对具有 IQueryable 或 IQueryable<T> 返回类型的操作启用查询支持。
// 若要避免处理意外查询或恶意查询,请使用 QueryableAttribute 上的验证设置来验证传入查询。
// 有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=279712。
//config.EnableQuerySupport();
// 若要在应用程序中禁用跟踪,请注释掉或删除以下代码行
// 有关详细信息,请参阅: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
}
以上的MapHttpAttributeRoutes方法只在ASP.NET Web API较新的版本中才有,如果你的版本比较低,可以通过"NuGet程序包管理器控制台"卸载旧版本,安装最新版本。
Uninstall-Package microsoft.aspnet.webapi –Force
install-package microsoft.aspnet.webapi
接下来,在Global.asax中,需要把原先注册WebApiConfig的方式注释掉,采纳新的方式,如下:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//WebApiConfig.Register(GlobalConfiguration.Configuration);
//Web API,启动特性路由
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
这时候运行项目可能会报如下错误:
这是因为在下载使用ASP.NET Web API最新版本的时候,顺带下载了一个最新版本的icrosoft.AspNet.WebApi.HelpPage。可以把最新版的HelpPage卸载掉,再下载相对老的版本。
Uninstall-Package Microsoft.AspNet.WebApi.HelpPage –Force
Install-Package Microsoft.AspNet.WebApi.HelpPage -Pre
使用Route特性
创建一个Cusomter类。
namespace MyRoutingAttributes4.Models
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
}
创建一个Order类。
namespace MyRoutingAttributes4.Models
{
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
}
创建一个Database类,用来获取Order集合。
using System.Collections.Generic;
using System.Linq;
using MyRoutingAttributes4.Models;
namespace MyRoutingAttributes4
{
public class Database
{
public static IEnumerable<Order> GetOrdersByCustomerId(int customerId)
{
return GetOrders().Where(o => o.CustomerId == customerId);
}
private static IEnumerable<Order> GetOrders()
{
Customer cus1 = new Customer() { Id = 1, Name = "张三" };
Customer cus2 = new Customer() { Id = 2, Name = "李四" };
List<Order> orders = new List<Order>()
{
new Order(){Id = 1, Total = 80M, CustomerId = 1, Customer = cus1},
new Order(){Id = 2, Total = 100M, CustomerId = 1, Customer = cus1},
new Order(){Id = 3, Total = 120M, CustomerId = 2, Customer = cus2}
};
return orders;
}
}
}
创建一个空的API控制器,编写如下:
using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;
namespace MyRoutingAttributes4.Controllers
{
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId)
{
return Database.GetOrdersByCustomerId(customerId);
}
}
}
在浏览器中输入如下:
如果你使用的是ASP.NET MVC4进行开发,在程序第一次运行的时候,可能会报如下错误:
[A]System.Web.WebPages.Razor.Configuration.HostSection 无法强制转换为 [B]System.Web.WebPages.Razor.Configuration.HostSection。类型 A 源自“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”(在上下文“Default”中的“C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Web.WebPages.Razor/v4.0_2.0.0.0__31bf3856ad364e35/System.Web.WebPages.Razor.dll”位置处)。类型 B 源自“System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”(在上下文“Default”中的“C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/vs/feb7ce97/a525d58a/asse
这是因为,在下载最新版本的ASP.NET Web API的时候,用到了Razor的最新版本。需要在根目录下的Web.config中作如下配置:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
......
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
使用RoutePrefix特性
如果想给某个API控制器中的所有Action加上一个前缀,可把RoutePrefix特性打在API控制器上。
比如我们希望是这样的格式:http://localhost/api/customers/1/orders
这样来修改OrdersController。
using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;
namespace MyRoutingAttributes4.Controllers
{
[RoutePrefix("api")]
public class OrdersController : ApiController
{
[Route("customers/{customerId}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId)
{
return Database.GetOrdersByCustomerId(customerId);
}
}
}

还可以在Route特性中使用~来重写Action的前缀规则。
using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;
namespace MyRoutingAttributes4.Controllers
{
[RoutePrefix("api")]
public class OrdersController : ApiController
{
[Route("~/myapi/customers/{customerId:int}/orders")]
[HttpGet]
public IEnumerable<Order> FindOrdersByCustomer(int customerId)
{
return Database.GetOrdersByCustomerId(customerId);
}
}
}

RoutePrefix特性定义的前缀还可以带参数变量:
[RoutePrefix("api/{customerId}")]
public class OrdersController : ApiController
路由约束
可以通过"{参数变量名称:约束}"来约束路由中的参数变量。
[Route("users/{id:int}"]
public User GetUserById(int id) { ... }
[Route("users/{name}"]
public User GetUserByName(string name) { ... }
以上,如果片段变量id为int类型,就路由到第一个Action,如果不是,路由到第二个Action。
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(正则表达式)}
可以为一个参数变量同时设置多个约束:
[Route("api/{id:int:min(1)}")]
实现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)
{
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));
config.MapHttpAttributeRoutes(constraintResolver);
}
}
使用自定义约束。
[Route("{id:nonzero}")]
可选参数及其默认值
如果一个路由参数变量是可选的,同时必须给该参数一个默认值。
[Route("api/{id:int?}")]
public IEnumerable<T> Get(int id = 8){}
在约束后面加?,表示可选,在方法参数中给id设置默认值。
给路由设置名称
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;
}
}
路由优先顺序
Route特性设置的路由优先顺序是根据惯例和RouteOrder属性来确定的。
惯例是:
1、静态片段变量
2、带约束的片段变量
3、不带约束的片段变量
4、带约束的通配符片段变量
5、不带约束的通配符片段变量
RouteOrder属性的默认值是0,属性值越小,排在越前面。
[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 = 1)]
public HttpResponseMessage GetPending() { ... }
[Route("{customerName}")] // unconstrained parameter
public HttpResponseMessage GetByCustomer(string customerName) { ... }
[Route("{*date:datetime}")] // wildcard
public HttpResponseMessage Get(DateTime date) { ... }
}
以上,路由的优先顺序是:
orders/details 静态片段变量,RouteOrder属性值为0
orders/{id} 带约束的片段变量,RouteOrder属性值为0
orders/{customerName} 不带约束的片段变量,RouteOrder属性值为0
orders/{*date} 带约束的通配符片段变量,RouteOrder属性值为0
orders/pending RouteOrder属性值为1
ASP.NET Web API实践系列04,通过Route等特性设置路由的更多相关文章
- ASP.NET Web API实践系列07,获取数据, 使用Ninject实现依赖倒置,使用Knockout实现页面元素和视图模型的双向绑定
本篇接着上一篇"ASP.NET Web API实践系列06, 在ASP.NET MVC 4 基础上增加使用ASP.NET WEB API",尝试获取数据. 在Models文件夹下创 ...
- ASP.NET Web API实践系列05,消息处理管道
ASP.NET Web API的消息处理管道可以理解为请求到达Controller之前.Controller返回响应之后的处理机制.之所以需要了解消息处理管道,是因为我们可以借助它来实现对请求和响应的 ...
- ASP.NET Web API实践系列09,在Fiddler和控制台中模拟GET和POST请求
ASP.NET Web API本质是由一个进程托管的一组类,需要宿主,这个宿主可以是ASP.NET应用程序,可以是MVC项目,可以是控制台应用程序,也可以是自己定制的宿主. 在VS2012中创建一个& ...
- ASP.NET Web API实践系列06, 在ASP.NET MVC 4 基础上增加使用ASP.NET WEB API
本篇尝试在现有的ASP.NET MVC 4 项目上增加使用ASP.NET Web API. 新建项目,选择"ASP.NET MVC 4 Web应用程序". 选择"基本&q ...
- ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置
ASP.NET Web API的路由和ASP.NET MVC相似,也是把路由放在RouteTable中的.可以在App_Start文件夹中的WebApiConfig.cs中设置路由模版.默认的路由模版 ...
- ASP.NET Web API实践系列02,在MVC4下的一个实例, 包含EF Code First,依赖注入, Bootstrap等
本篇体验在MVC4下,实现一个对Book信息的管理,包括增删查等,用到了EF Code First, 使用Unity进行依赖注入,前端使用Bootstrap美化.先上最终效果: →创建一个MVC4项目 ...
- ASP.NET Web API实践系列01,以ASP.NET Web Form方式寄宿
创建一个空的ASP.NET Web Form项目. 右键项目,添加新项,创建Web API控制器类,TestController. 删除掉TestController默认的内容,编写如下: using ...
- ASP.NET Web API实践系列11,如何设计出优秀的API
本篇摘自:InfoQ的微信公众号 在设计API的时候考虑的问题包括:API所使用的传输协议.支持的消息格式.接口的控制.名称.关联.次序,等等.我们很难始终作出正确的决策,很可能是在多次犯错之后,并从 ...
- ASP.NET Web API 2系列(三):查看WebAPI接口的详细说明及测试接口
引言 前边两篇博客介绍了Web API的基本框架以及路由配置,这篇博客主要解决在前后端分离项目中,为前端人员提供详细接口说明的问题,主要是通过修改WebApi HelpPage相关代码和添加WebAp ...
随机推荐
- Magento架构师的笔记-----Magento显示当前目录的父分类和子分类的分类名
在Magento目录的分类页面里,希望在左侧导航获取到父分类和子分类,可以用以下方法:打开app/your_package/your_themes/template/catalog/navigatio ...
- 转:Bat命令学习
转:http://www.cnblogs.com/SunShineYPH/archive/2011/12/13/2285570.html一.基础语法: 1.批处理文件是一个“.bat”结尾的文本文件, ...
- C# string[,]与string[][]的区别
对于这两者的区别: 1.入门:string[,]可读可写,而string[][]与string[]相同,不可对第二位进行写操作 static void Main(string[] args) { // ...
- 关于Highcharts图表组件动态修改属性的方法(API)总结之Series
Highcharts图表组件内的Series很重要,如果说Categries是其大脑,那么Series就是其心脏.这两者才是Highcharts图表组件的重中之重啊.接下来就为大家介绍关于设置Seri ...
- iOS学习笔记---oc语言第六天
Block .数组高级 block本质上就是匿名函数(没有名称的函数) block语法和函数指针很相似 回顾函数 函数:C语⾔中,实现某一类功能的代码段. 完整的函数包含两部分:函数声明.函数定义 函 ...
- Java-->实现群聊功能(C/S模式--TCP协议)
--> Java 对TCP协议的支持: --> java.net包中定义了两个类ServerSocket 和Socket ,分别用来实现双向连接的server 端和client 端. -- ...
- 图像处理之image stitching
背景介绍 图像拼接是一项应用广泛的图像处理技术.根据特征点的相互匹配,可以将多张小视角的图像拼接成为一张大视角的图像,在广角照片合成.卫星照片处理.医学图像处理等领域都有应用.早期的图像拼接主要是运用 ...
- sublime text 2 + Dev-C++/MinGW 组合配置更方便快捷的 C/C++ 编译环境(原创)
首先看一下配置后的效果: 1.直接在底部文本框中显示运行结果(不需要从键盘输入的时候使用): 2.在cmd中运行结果(需要从键盘输入的时候使用): 快捷键说明: 运行: 在底部文本栏显示结果:Ctrl ...
- (单选后,显示相对应的div)点击免费没有变化,点击收费出现输入框
<li> <label class="feiyonglabel">活动费用</label> <div class="textbo ...
- webStorm快捷键总结
Ctrl+Shift+a:快速查找使用编辑器所有功能1.左侧栏目录显影:Ctrl+Shift+F122.文件模板配置:File>Settings>Editor>File and Co ...