快速入門系列--WebAPI--03框架你值得擁有


    接下來進入的是俺在ASP.NET學習中最重要的WebAPI部分,在現在流行的互聯網場景下,WebAPI可以和HTML5、單頁應用程序SPA等技術和理念很好的結合在一起。所謂ASP.NET WebAPI,其核心概念就是構建REST風格的Web服務,把一起數據視為資源,無論是服務請求或者是數據操作,與以前的SOAP和XML-RPC架構風格有很大不同。說道這,很多讀者可能想到WCF中不是早都有了REST風格的服務么,為什么還需要這個WebAPI?確實如此,不過WCF中的該類型服務顯得比較復雜,因為其通信管道的構成由於集成了多種不同的通信協議,自然的其基礎程序集就顯得非常的龐大臃腫。

    簡單來說,WebAPI就是簡單高效,"你值得擁有"!讓我們通過臨摹蔣老師的例子對它有個初步的了解,后端代碼如下:

 1 public class ContactsController : ApiController
 2 {
 3 private static IList<Contact> contacts = new List<Contact>
 4 {
 5 new Contact{
 6 Id="001",
 7 Name="Xixi",
 8 PhoneNo="12132432",
 9 EmailAddress="xixi@gmail.com"
10 },
11 new Contact{
12 Id="002",
13 Name="XiongEr",
14 PhoneNo="312",
15 EmailAddress="XiongEr@gmail.com"
16 }
17 };
18 public IEnumerable<Contact> Get()
19 {
20 return contacts;
21 }
22 public Contact Get(string id)
23 {
24 return contacts.FirstOrDefault(c => c.Id == id);
25 }
26 public void Put(Contact contact)
27 {
28 contact.Id = Guid.NewGuid().ToString();
29 contacts.Add(contact);
30 }
31 public void Post(Contact contact)
32 {
33 Delete(contact.Id);
34 contacts.Add(contact);
35 }
36 public void Delete(string id)
37 {
38 Contact tempContact = contacts.FirstOrDefault(c => c.Id == id);
39 contacts.Remove(tempContact);
40 }
41 }
View Code

前端代碼如下:

 1 <html>
 2 <head>
 3 <title>聯系人管理</title>
 4 <script type="text/javascript" src="~/Scripts/jquery-1.8.2.js"></script>
 5 <script type="text/javascript" src="~/Scripts/knockout-2.2.0.js"></script>
 6 </head>
 7 <body>
 8 <div id="contacts">
 9 <table>
10 <tr>
11 <th>姓名</th>
12 <th>電話號碼</th>
13 <th>Email地址</th>
14 <th></th>
15 </tr>
16 <tbody>
17 <!-- ko foreach: allContacts -->
18 <tr>
19 <td data-bind="text: Name" />
20 <td data-bind="text: PhoneNo" />
21 <td>
22 <input type="text" class="textbox long" data-bind="value: EmailAddress" />
23 </td>
24 <td>
25 <a href="#" data-bind="click: $root.updateContact">修改</a>
26 <a href="#" data-bind="click: $root.deleteContact">刪除</a>
27 </td>
28 </tr>
29 <!-- /ko -->
30 <tr data-bind="with: addedContact">
31 <td>
32 <input type="text" class="textbox" data-bind="value: Name" /></td>
33 <td>
34 <input type="text" class="textbox" data-bind="value: PhoneNo" /></td>
35 <td>
36 <input type="text" class="textbox long" data-bind="value: EmailAddress" /></td>
37 <td><a href="#" data-bind="click: $root.addContact" />添加</td>
38 </tr>
39 </tbody>
40 </table>
41 </div>
42 <script type="text/javascript">
43 function ContactViewModel() {
44 self = this;
45 self.allContacts = ko.observableArray();
46 self.addedContact = ko.observable();
47 //加載聯系人列表
48 self.loadContacts = function () {
49 $.get("/api/contacts", null, function (data) {
50 self.allContacts(data);
51 var emptyContact = { Id: "", Name: "", PhoneNo: "", EmailAddress: "" };
52 self.addedContact(emptyContact);
53 });
54 }
55 //添加聯系人
56 self.addContact = function (data) {
57 if (!self.validate(data)) {
58 return;
59 }
60 $.ajax({
61 url: "/api/contacts/",
62 data: self.addedContact(),
63 type: "PUT",
64 success: self.loadContacts
65 });
66 };
67 //修改聯系人
68 self.updateContact = function (data) {
69 $.ajax({
70 url: "/api/contacts/",
71 data: data,
72 type: "POST",
73 success: self.loadContacts
74 });
75 };
76 //刪除聯系人
77 self.deleteContact = function (data) {
78 $.ajax({
79 url: "/api/contacts/" + data.Id,
80 type: "DELETE",
81 success: self.loadContacts
82 });
83 };
84 self.validate = function (data) {
85 if (data.Name && data.PhoneNo && data.EmailAddress) {
86 return true;
87 }
88 alert("請輸入完整聯系人信息!");
89 return false;
90 }
91 self.loadContacts();
92 }
93 ko.applyBindings(new ContactViewModel());
94 </script>
95 </body>
96 </html>
View Code

    這個像補充的是,蔣老師在這用的是自帶的knockoutJS作為MVVM風格的部分前端框架。關於這一塊,有一個問題困擾了我很久,就是KnockoutJS和AngularJS誰的適用性更強,其實它們沒有可比性,KnockoutJS只提供了部分的工作。以下鏈接是對此問題的解釋,結論是我將學習並使用AngularJS。

http://blog.darkthread.net/post-2014-06-07-go-to-angularjs.aspx

說到這,我還想到了學習中的一個困惑,那么多的IOC框架到底哪個相對更好一些?結論是Autofac,它以被使用在Orchard開源的CMS系統中,順道提一嘴,nopCommerce的.net開源電商系統也不錯哦。之前IOC框架對比的詳情請見如下鏈接,李平老師做了最好的解釋:

http://www.cnblogs.com/liping13599168/archive/2011/07/17/2108734.html

    接下來,介紹ASP.NET WebAPI的服務器管道,這一塊和之前學習的ASP.NET MVC管道很相似,但也有一些差異,不過個人感覺這個管道更加的像J2EE的管道了。由於很多內容比較相似,將進行簡單的介紹,不過框架中異步編程模型用的很多,值得學習參考。下圖簡單的表述了框架對請求的處理過程:

    框架通過單例提供HttpControllerHandler對象,多個HttpWebRoute共享對象,並且它將創建右側的ASP.NET Web API處理管道,通過調用BeginProcessRequest方法激活管道運轉。該管道其實就是HttpMessgaeHandler鏈,HttpServer和HttpControllerDispatcher可以看做兩個特殊的HttpMessageHandler,接下來通過表格的形式對相關類型進行簡單的介紹:

類型 簡介
HttpMessageHandler 核心類,針對請求的處理實現在SendAsync中,針對響應的處理通過返回類型Task<HttpResponseMessage>完成
HttpRequestMessage Content屬性封裝Http主體信息
HttpResponseMessage StatusCode、ReasonPhrase屬性表示響應狀態碼與描述
DelegatingHandler 用於構建處理鏈,通過InnerHanlder屬性進行傳遞,是責任鏈模式的實現?
HttpServer Dispatcher屬性指向最終的分發器對象,Configuration屬性包含了所有的配置信息。
HttpConfiguration DependencyResolverFilters: AuthorizationFilter, ActionFilter, ExceptionFilterFormatters:返回格式化器列表IncludeErrorDetailPolicy:客戶端異常顯示策略PropertiesServices: 返回ServiceContainer,一個簡易IocR容器,默認實現為DefaultServices,很常用。

    HttpControllerHandler以延遲加載的方式來創建HttpServer,字典屬性Properties以Key為"MS_HttpContext"、"MS_HttpRouteData"的形式傳遞相關數據。HttpControllerDispatcher負責最后對請求做最后的處理,包括對ApiController的激活和目標Action的執行等操作,用下表簡述該過程:

行為 簡介
HttpController的激活 借助HttpControllerDescriptor,完成HttpController類型解析、選擇、創建等操作,可以通過自定義DependencyResolver或HttpControllerActivator來實現基於IOC的HttpController的激活。
HttpController的執行 通過ExecuteAsync方法,參數為HttpControllerContext,注意UrlHelper中Link代表絕對地址,Route相對地址
Action的選擇 HttpActionDescriptor的ExecuteAsync方法實現Action的執行,Action支持7中不同的HTTP方法,默認為POST。通過HttpActionSelector組件實現對目標Action的選擇,方法GetActionMapping的返回值為一個ILookup<string, HttpActionDescriptor>類型
Model元數據的解析 與MVC基本一致
Action參數綁定 借助HttpParameterDescriptor、HttpActionBinding,通過HttpParameterBinding對象的ExecuteBindingAsync完成綁定,具體的實現類有: CancellationTokenParameterBinding ErrorParameterBinding FomatterParameterBinding:消息主體,html,json,xml HttpRequestParameterBinding:HttpRequestMessage ModelBinderParameterBinding:查詢字符串,路由數據
Model的驗證 包括DataAnnotationModelValidator RequiredMemberModelValidator ValidatableObjectAdapter ErrorModelValidator等驗證器,需要注意的是該框架中驗證過程是遞歸的,與MVC有點不同。
Action的執行與結果的響應 通過HttpActionInvoker的InvokerActionAsync方法激活Action,通過ActionResultConverter將Action的返回值轉換為HttpResponseMessage,轉換器包括: ResponseMessageResultConverter ValueResultConverter<T> VoidResultConverter 3個內置Filter篩選器的作用與MVC中的類似

補上IOC實現的代碼和HttpParameterBinding的流程圖:

 1 public class NinjectDependencyResolver : IDependencyResolver
 2 {
 3 private List<IDisposable> disposableServices = new List<IDisposable>();
 4 public IKernel Kernel { get; private set; }
 5 public NinjectDependencyResolver(NinjectDependencyResolver parent)
 6 {
 7 this.Kernel = parent.Kernel;
 8 }
 9 public NinjectDependencyResolver()
10 {
11 this.Kernel = new StandardKernel();
12 }
13 public void Register<TFrom, TTo>() where TTo : TFrom
14 {
15 this.Kernel.Bind<TFrom>().To<TTo>();
16 }
17 public IDependencyScope BeginScope()
18 {
19 return new NinjectDependencyResolver(this);
20 }
21 public object GetService(Type serviceType)
22 {
23 var service = this.Kernel.TryGet(serviceType);
24 this.AddDisposableService(service);
25 return service;
26 }
27 public IEnumerable<object> GetServices(Type serviceType)
28 {
29 foreach (var service in this.Kernel.GetAll(serviceType))
30 {
31 this.AddDisposableService(service);
32 yield return service;
33 }
34 }
35 public void Dispose()
36 {
37 foreach (var disposable in disposableServices)
38 {
39 disposable.Dispose();
40 }
41 }
42 private void AddDisposableService(object service)
43 {
44 IDisposable disposable = service as IDisposable;
45 if (null != disposable && !disposableServices.Contains(disposable))
46 {
47 disposableServices.Add(disposable);
48 }
49 }
50 }
51 public class WebApiApplication : System.Web.HttpApplication
52 {
53 protected void Application_Start()
54 {
55 //自定義操作
56 NinjectDependencyResolver dependencyResolver = new NinjectDependencyResolver();
57 dependencyResolver.Register<IContactRepository, DefaultContactRepository>();
58 GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;
59 }
60 }
View Code

HttpParameterBinding流程圖:

    最后介紹與WebAPI客戶端調用相關的內容,提到調用大家第一反應就是在Web頁面中通過javascript進行Ajax調用,獲取數據並呈現,服務的消費者是前端頁面,這只是調用的主要方式之一。另外一種就是通過HttpClient來進行調用,這和Web Service調用很相似,服務的消費者是一般應用程序。HttpClient類繼承之抽象類HttpMessageInvoker,核心方法SendAsync包括HttpRequestMessage的參數和HttpResponseMessage的返回類型,和之前服務器端的HttpMessageHandler類型一樣,實際上HttpClient就是一個該類的封裝。HttpCompletionOption用於設置響應完成的標志,包括讀完消息頭和讀完消息體。屬性BaseAddress用於指定WebAPI基地址,DefaultRequestHeader用於添加任意的報頭,MaxResponseContentBufferSize表示讀取緩存區的大小,默認2G,Timeout表示超時時限,默認100s。GetAsync, GetByteArrayAsync, GetStreamAsync, GetStringAsync用於HTTP-GET請求,其他方法也有相似定義。下面通過一個服務器端自我寄宿,客戶端一般調用的例子完成學習,需要注意通過Nuget添加SelfHost和Client的庫,代碼如下所示:

 1 //服務器端
 2 class Program
 3 {
 4 static void Main(string[] args)
 5 {
 6 var config = new HttpSelfHostConfiguration("http://127.0.0.1:3721");
 7 config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
 8 using (var httpServer = new HttpSelfHostServer(config))
 9 {
10 httpServer.OpenAsync().Wait();
11 Console.WriteLine("按任意鍵關閉WebAPI");
12 Console.Read();
13 }
14 }
15 }
16 //客戶端
17 class Program
18 {
19 static void Main(string[] args)
20 {
21 Uri baseAddress = new Uri("http://127.0.0.1:3721");
22 var httpClient = new HttpClient { BaseAddress = baseAddress };
23 IEnumerable<Contact> contacts = httpClient.GetAsync("api/contacts").Result.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
24 Console.WriteLine("當前聯系人列表:");
25 ListContacts(contacts);
26 var contact = new Contact { Id = "003", Name = "qiuzi", EmailAddress = "qiuqiu@gmail.com", PhoneNo = "95580" };
27 Console.WriteLine("\n添加聯系人003: ");
28 httpClient.PutAsync<Contact>("/api/contacts", contact, new JsonMediaTypeFormatter()).Wait();
29 contacts = httpClient.GetAsync("api/contacts").Result.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
30 ListContacts(contacts);
31 contact = new Contact { Id = "003", Name = "qiuzi", EmailAddress = "zhaoyun@outlook.com", PhoneNo = "123" };
32 Console.WriteLine("\n修改聯系人003: ");
33 httpClient.PostAsync<Contact>("/api/contacts", contact, new XmlMediaTypeFormatter()).Wait();
34 contacts = httpClient.GetAsync("api/contacts").Result.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
35 ListContacts(contacts);
36 Console.WriteLine("\n刪除聯系人003: ");
37 httpClient.DeleteAsync("/api/contacts/003").Wait();
38 contacts = httpClient.GetAsync("api/contacts").Result.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
39 ListContacts(contacts);
40 Console.Read();
41 }
42 
43 private static void ListContacts(IEnumerable<Contact> contacts)
44 {
45 foreach (var contact in contacts)
46 {
47 Console.WriteLine("{0, -6}{1, -6}{2, -20}{3, -10}", contact.Id, contact.Name, contact.EmailAddress, contact.PhoneNo);
48 }
49 }
50 }
View Code

 此外,WebAPI學習系列目錄如下,歡迎您的閱讀!

快速入門系列--WebAPI--01基礎

快速入門系列--WebAPI--02進階

快速入門系列--WebAPI--03框架你值得擁有

快速入門系列--WebAPI--04在老版本MVC4下的調整

 

注:本文主要供自己學習,不妥之處望見諒。

參考資料:

[1]蔣金楠. ASP.NET MVC4框架揭秘[M]. 上海:電子工業出版社, 2012. 445-526


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM