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); } } 這時候運行項目可能會報如下錯誤: 1 這是因為在下載使用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); } } } 在瀏覽器中輸入如下: 2 如果你使用的是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); } } } 3 還可以在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); } } } 4 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
裝載 僅供保存學習