不管是因為什么原因,結果是在新出的MVC中,增加了WebAPI,用於提供REST風格的WebService,個人比較喜歡REST風格的WebService,感覺比SOAP要輕量級一些,而且對客戶端的要求也更少,更符合網絡數據傳輸的一般模式,客戶端完全擺脫了代理和管道來直接和WebService進行交互,具體的區別可以參見Web 服務編程,REST 與 SOAP
(一)環境准備
本機的環境是XP+VS2010,需要安裝VS2010 SP1升級包,MVC4升級包,Vs2010安裝SP1后會影響SQLServer2008的自動提示功能,需要在安裝補丁或插件,安裝成功后可以新建如下的 MVC WebAPI 項目
(二)概覽
新生成的WebAPI項目和典型的MVC項目一樣,包含主要的Models,Views,Controllers等文件夾和Global.asax文件
Views對於WebAPI來說沒有太大的用途,Models中的Model主要用於保存Service和Client交互的對象,這些對象默認情況下會被轉換為Json格式的數據進行傳輸,Controllers中的Controller對應於WebService來說是一個Resource,用於提供服務。和普通的MVC一樣,Global.asax用於配置路由規則
(三)Models
和WCF中的數據契約形成鮮明對比的是,MVC WebAPI中的Model就是簡單的POCO,沒有任何別的東西,如,你可以創建如下的Model
public class TestUseMode { public string ModeKey{get;set;} public string ModeValue { get; set; } }
注意:Model必須提供public的屬性,用於json或xml反序列化時的賦值
(四)Controllers
MVC WebAPI中的Controllers和普通MVC的Controllers類似,不過不再繼承於Controller,而改為繼承API的ApiController,一個Controller可以包含多個Action,這些Action響應請求的方法與Global中配置的路由規則有關,在后面結束Global時統一說明
(五)Global
默認情況下,模板自帶了兩個路由規則,分別對應於WebAPI和普通MVC的Web請求,默認的WebAPI路由規則如下
1 routes.MapHttpRoute( 2 name: "DefaultApi", 3 routeTemplate: "api/{controller}/{id}", 4 defaults: new { id = RouteParameter.Optional } 5 );
可以看到,默認路由使用的固定的api作為Uri的先導,按照微軟官方的說法,用於區分普通Web請求和WebService的請求路徑:
Note: The reason for using "api" in the route is to avoid collisions with ASP.NET MVC routing. That way, you can have "/contacts" go to an MVC controller, and "/api/contacts" go to a Web API controller. Of course, if you don't like this convention, you can change the default route table.
可以看到,默認的路由規則只指向了Controller,沒有指向具體的Action,因為默認情況下,對於Controller中的Action的匹配是和Action的方法名相關聯的:
具體來說,如果使用上面的路由規則,對應下面的Controller:
public class TestController : ApiController { public static List<TestUseMode> allModeList = new List<TestUseMode>(); public IEnumerable<TestUseMode> GetAll() { return allModeList; } public IEnumerable<TestUseMode> GetOne(string key) { return allModeList.FindAll((mode) => { if (mode.ModeKey.Equals(key)) return true; return false; }); } public bool PostNew(TestUseMode mode) { allModeList.Add(mode); return true; } public int Delete(string key) { return allModeList.RemoveAll((mode) => { if (mode.ModeKey == key) return true; return false; }); } public int DeleteAll() { return allModeList.RemoveAll((mode) => { return true; }); } public int PutOne(string key, string value) { List<TestUseMode> upDataList = allModeList.FindAll((mode) => { if (mode.ModeKey == key) return true; return false; }); foreach(var mode in upDataList) { mode.ModeValue = value; } return upDataList.Count; } }
則,會有下面的對應關系:
簡單使用JS調用上面提供的數據接口
1 function getAll() { 2 $.ajax({ 3 url: "api/Test/", 4 type: 'GET', 5 success: function (data) { 6 document.getElementById("modes").innerHTML = ""; 7 $.each(data, function (key, val) { 8 var str = val.ModeKey + ': ' + val.ModeValue; 9 $('<li/>', { html: str }).appendTo($('#modes')); 10 }); 11 } 12 }).fail( 13 function (xhr, textStatus, err) { 14 alert('Error: ' + err); 15 }); 16 } 17
18
19
20 function add() { 21
22 $.ajax({ 23 url: "api/Test/", 24 type: "POST", 25 dataType: "json", 26 data: { "ModeKey": document.getElementById("txtKey").value, "ModeValue": document.getElementById("txtValue").value }, 27 success: function (data) { 28 getAll(); 29 } 30 }).fail( 31 function (xhr, textStatus, err) { 32 alert('Error: ' + err); 33 }); 34
35 } 36
37 function find() { 38
39 $.ajax({ 40 url: "api/Test/" + document.getElementById("txtFindKey").value, 41 type: 'GET', 42 success: function (data) { 43 document.getElementById("modes").innerHTML = ""; 44 $.each(data, function (key, val) { 45 var str = val.ModeKey + ': ' + val.ModeValue; 46 $('<li/>', { html: str }).appendTo($('#modes')); 47 }); 48 } 49 }).fail( 50 function (xhr, textStatus, err) { 51 alert('Error: ' + err); 52 }); 53 } 54
55 function removeAll() { 56 $.ajax({ 57 url: "api/Test/", 58 type: 'DELETE', 59 success: function (data) { 60 document.getElementById("modes").innerHTML = ""; 61 getAll(); 62 } 63 }).fail( 64 function (xhr, textStatus, err) { 65 alert('Error: ' + err); 66 }); 67 } 68
69 function remove() { 70 $.ajax({ 71 url: "api/Test/"+document.getElementById("txtRemoveKey").value, 72 type: 'DELETE', 73 success: function (data) { 74 document.getElementById("modes").innerHTML = ""; 75 getAll(); 76 } 77 }).fail( 78 function (xhr, textStatus, err) { 79 alert('Error: ' + err); 80 }); 81 } 82
83 function update() { 84 $.ajax({ 85 url: "api/Test/", 86 type: 'PUT', 87 dataType: "json", 88 data: { "key": document.getElementById("txtUpdateKey").value, "value": document.getElementById("txtUpdateValue").value }, 89 success: function (data) { 90 document.getElementById("modes").innerHTML = ""; 91 getAll(); 92 } 93 }).fail( 94 function (xhr, textStatus, err) { 95 alert('Error: ' + err); 96 }); 97 }
這樣就實現了最基本的CRUD操作。
(六)路由規則擴展
和普通的MVC一樣,MVC WebAPI支持自定義的路由規則,如:在上面的操作中,路由規則使用
"api/{controller}/{id}"
則限定了使用GET方式利用URL來傳值時,controller后面的接收參數名為id,但是在Controller中,GetOne方法的接收參數名為key,是不會被匹配的,這是只需要新增一個新的路由規則,或修改原先的路由規則為:
"api/{controller}/{key}"
當然,可以對路由進行更深的擴展,如:擴展成和普通MVC一樣的路由:
"api/{controller}/{action}/{id}"
這樣,就要求同時使用Action和HTTP方法進行匹配
當然,根據微軟的說法,這種使用是不被推薦的,因為這不符合大家對WebService的一般認知:
For a RESTful API, you should avoid using verbs in the URIs, because a URI should identify a resource, not an action.
(七)使用Attribute聲明HTTP方法
有沒有感覺默認的使用方法名來匹配HTTP Method的做法很傻??或者我有一些方法是自己用的,不想暴露出來,又該怎么辦?還是使用attribute做這些工作感覺優雅一些,比如,上面的Action我可以更改為:
[HttpGet] public IEnumerable<TestUseMode> FindAll() [HttpGet] public IEnumerable<TestUseMode> FindByKey(string key) [HttpPost] public bool Add(TestUseMode mode) [HttpDelete] public int RemoveByKey(string key) [HttpDelete] public int RemoveAll() [HttpPut] public int UpdateByKey(string key, string value) [NonAction] public string GetPrivateData()
當然,我只列出了方法名,而不是這些方法真的沒有方法體...方法體是不變的,NoAction表示這個方法是不接收請求的,即使以GET開頭。
如果感覺常規的GET,POST,DELETE,PUT不夠用,還可以使用AcceptVerbs的方式來聲明HTTP方法,如:
[AcceptVerbs("MKCOL", "HEAD")] public int UpdateByKey(string key, string value) { List<TestUseMode> upDataList = allModeList.FindAll((mode) => { if (mode.ModeKey == key) return true; return false; }); foreach(var mode in upDataList) { mode.ModeValue = value; } return upDataList.Count; }
******************************************************************************
作者:王坤
出處:http://www.cnblogs.com/wk1234
本文版權歸 王坤和博客園共有,歡迎轉載,但請注明出處。
