系列導航地址http://www.cnblogs.com/fzrain/p/3490137.html
題外話:由於這個技術點是新學的,並不屬於原系列,但借助了原系列的項目背景,故命名外傳系列,以后也可能在這個系列中附加一些新的技術。
前言
在Web Api 2.0中,提出了一種新的配置路由方式——基於特性的路由(Attribute-based Routing),在我們之前介紹的配置路由方式稱為——基於公約的路由(Convention-based Routing),新的路由配置方式同樣應用在MVC5中,因此本文就來介紹一下基於特性的路由。
在之前的一篇文章中,我們處理了這么一個業務——實現學生選課。我們是通過在“WebApiConfig”定制了一條路由數據來實現的,這條路由實現了選課以及根據課程Id來查詢選擇選擇該課程的所有學生信息,感覺設計還可以。在實際應用中一般來說查詢用的是最多的,使用Attribute Routing來注冊路由會更靈活,控制起來也更方便,更符合Rest。本文以根據課程的名字來查詢所有選擇該課程的學生信息以及根據課程名字以及學生名字查詢某一學生的信息。
基於特性的路由介紹
顧名思義,新路由將使用一個特性來實現路由注冊,如下:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=true, Inherited=true)] public sealed class RouteAttribute : Attribute, IHttpRouteInfoProvider { public RouteAttribute(); public RouteAttribute(string template); public string Name { get; set; } public int Order { get; set; } public string Template { get; private set; } }
這個類包含3個屬性:Name指路由的名字,Order是指路由的順序,Template就是我們要去匹配URL的模板
實現Attribute Routing
原理性的東西不多介紹的(我也沒多研究 呵呵),能把學到的技術運用到實際中才是王道,上來一大堆原理容易暈,以后需要深入研究再看原理。
在“EnrollmentsController”中新增一個方法GetStudentsInfo:
[Route("api/enrollments/{courseName}/{studentName?}")] public IEnumerable<StudentBaseModel> GetStudentsInfo(string courseName, string studentName="") { IQueryable<Student> query; Course course= TheRepository.GetAllCourses().Where(c => c.Name == courseName).FirstOrDefault(); if (course==null ) { return null ; } query = TheRepository.GetEnrolledStudentsInCourse(course.Id).OrderBy(s => s.LastName); if (!string.IsNullOrWhiteSpace(studentName)) { query = query.Where(s => s.FirstName == studentName); } var totalCount = query.Count(); System.Web.HttpContext.Current.Response.Headers.Add("X-InlineCount", totalCount.ToString()); var results = query .ToList() .Select(s => TheModelFactory.Create(s)); return results; }
在我們的Action上使用了RouteAttribute。分析一下這個URL模板("api/enrollments/{courseName}/{studentName?}"),{courseName}會匹配到Action的courseName參數上,對於Action的另一個參數studentName是一個可選參數,也就是說請求中沒有給出值那么就是默認的空字符串,有值得話就會被賦值,因此我們在“{studentName}”后面加上? 標記為可選的URI參數。
ok,就這么簡單,測試一次:
結果:
呃,出錯了。。。
一個新的問題
出錯了,不過不管怎么說解決方案總歸是有的,首先看下錯誤原因:是在LearningControllerSelector類的方法中出現了空引用,那么我們就不得不看下這個方法:
public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { var controllers = GetControllerMapping(); //Will ignore any controls in same name even if they are in different namepsace var routeData = request.GetRouteData(); var controllerName = routeData.Values["controller"].ToString(); HttpControllerDescriptor controllerDescriptor; if (controllers.TryGetValue(controllerName, out controllerDescriptor)) { var version = "2"; var versionedControllerName = string.Concat(controllerName, "V", version); HttpControllerDescriptor versionedControllerDescriptor; if (controllers.TryGetValue(versionedControllerName, out versionedControllerDescriptor)) { return versionedControllerDescriptor; } return controllerDescriptor; } return null; }
看了這段代碼我們不難發現這個方法是我們重寫了基類的方法,目的是為了實現現版本控制(詳情可移步:http://www.cnblogs.com/fzrain/p/3558765.html),在我們重寫的方法中我們用到了RouteData中包含的Controller的名字,而這個名字是由基於公約的路由與URI匹配得到的,因此我們這里肯定是沒有的。因為我們的項目混合了2鍾路由配置,而自定義的方法是針對基於公約的路由配置,因此對於Attribute Routing我們直接使用默認的選擇方式:
在“var controllerName = routeData.Values["controller"].ToString(); ”代碼之前加一個判斷:
if (string.IsNullOrWhiteSpace(routeData.Route.RouteTemplate)) { return base.SelectController(request); }
再次請求就會得到如下結果:
結果正確,解決方案感覺還可以再考慮考慮,大家有什么好的建議可以提。但是不管怎么說,有一點是明確的:對於基於特性的路由規則,不需要包含Controller的名字,在本案列中感覺上是尋找Acton上的路由模板來匹配請求的URI,然后確定對應的Action。
總結
基於特性路由(Attribute Routing)是一種新的定制路由規則方式,與傳統的配置方式相比更靈活,但同時比較分散,在web api中,2鍾路由配置方式可以共存,所以本文只是介紹一種新的配置方式,做項目時也可以多一種選擇。
本文參考相關鏈接:http://www.cnblogs.com/aehyok/p/3449851.html