轉載:http://www.th7.cn/Program/net/201410/302571.shtmlhtml
ASP.NET Web API路由,簡單來講,就是把客戶端請求映射到對應的Action上的過程。在"ASP.NET Web API實踐系列03,路由模版, 路由慣例, 路由設置"一文中,體驗了經過模版、慣例、HTTP方法來設置路由,這種作法的好處是把路由模版統一放在了App_Start文件夾下的WebApiConfig類中,方便管理,但缺點是不夠靈活。web
REST把一切都當作資源,有時候,一個資源連帶子資源,好比Customer和Orders密切關聯,咱們可能但願輸入這樣的請求:customers/1/orders,但僅僅憑藉慣例,很難實現這種路由。而實際上,ASP.NET Web API爲咱們準備了Route特性,該特性能夠直接打到Action上,使用很是靈活、直觀。正則表達式
下面就在ASP.NET MVC4下來體驗Route特性的使用方法。api
容許Route特性瀏覽器
首先須要在WebApiConfig中設置。asp.net
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程序包管理器控制檯"卸載舊版本,安裝最新版本。ui
Uninstall-Package microsoft.aspnet.webapi –Force
install-package microsoft.aspnet.webapi
接下來,在Global.asax中,須要把原先註冊WebApiConfig的方式註釋掉,採納新的方式,以下:
spa
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);
}
}
這時候運行項目可能會報以下錯誤:
.net
這是由於在下載使用ASP.NET Web API最新版本的時候,順帶下載了一個最新版本的icrosoft.AspNet.WebApi.HelpPage。能夠把最新版的HelpPage卸載掉,再下載相對老的版本。xml
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屬性來肯定的。
慣例是:
一、靜態片斷變量
二、帶約束的片斷變量
三、不帶約束的片斷變量
四、帶約束的通配符片斷變量
五、不帶約束的通配符片斷變量
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