參考頁面: http://www.yuanjiaocheng.net/webapi/media-formatter.html http://www.yuanjiaocheng.net/webapi/webapi-filters.html http://www.yuanjiaocheng.net/we ...
參考頁面:
http://www.yuanjiaocheng.net/webapi/media-formatter.html
http://www.yuanjiaocheng.net/webapi/webapi-filters.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-get.html
http://www.yuanjiaocheng.net/webapi/create-crud-api-1-post.html
寫在前面
ASP.NET Web API是一個框架,可以很容易構建達成了廣泛的HTTP服務客戶端,包括瀏覽器和移動設備。是構建RESTful應用程式的理想平臺的.NET框架。
上面是微軟對Web API給出的定義,其中包含兩個關鍵字:HTTP和RESTful,其實從這一方面,大家就可以看出Web API和它的同胞兄弟:WebService和WCF有些不同了。
HTTP
對於HTTP大家都不是很陌生,因為我們每天瀏覽網頁填寫的URL就是HTTP開頭,但只是知道有這個東西,確沒有想過它是什麼,就好像我們對世間萬物有著模糊的認識,但認識東西的確很少。
也可以從另一方面去理解,曾經看一個電視節目,有個嘉賓在錄製的過程中,突然抬起頭對著天花板仰望,然後主持人很驚訝問他在幹嗎?他說:我在思考宇宙有沒有盡頭?主持人接著問:那宇宙有沒有盡頭?他淡然的回答道:有就有,沒有就沒有。當然觀眾和主持人都笑作一團,你會笑嗎?透過現象看本質,其實這個思想和老莊的思想很合拍,扯遠了。
HTTP即超文本傳送協議。
超文本傳輸協議 (HTTP-Hypertext transfer protocol) 是一種詳細規定了瀏覽器和萬維網伺服器之間互相通信的規則,通過網際網路傳送萬維網文檔的數據傳送協議。
從定義中看出HTTP是一種協議,超文本傳輸協議,那什麼是超文本?和我們所說的富文本有些區別,超文本也是一種文本格式,那可以把它看成是文本與文本之間關聯而組成的網狀文本,有點類似於面向對象中對象的集合,雖然是對象的集合,但本身也是一個對象。
HTTP協議有下麵三個基本特點:
- 簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與伺服器聯繫的類型不同。由於HTTP協議簡單,使得HTTP伺服器的程式規模小,因而通信速度很快。
- 靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
- 無狀態:無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。
相對於Web API來說,HTTP不只是為了生成Web頁面,它也是一個強大的平臺建設公開服務和數據的API。HTTP是簡單、 靈活和無處不在,因此幾乎任何平臺都可以有一個HTTP庫,因此,HTTP服務可以到達範圍廣泛的客戶端,包括瀏覽器、移動設備和傳統的桌面應用程式。
RESTful
RESTful架構概念是Fielding提出的,Fielding這號人物就是上面HTTP協議的主要設計者之一。我們先看下RESTful這個詞,ful是跟在名詞之後,表示程度,什麼什麼的,例如helpful樂於助人的,因此我們可以看出符合REST的架構就可以稱為RESTful,接著我們看下REST,全稱為“Representational State Transfer”,意為“表現層狀態轉化”。
在符合架構原理的前提下,理解和評估以網路為基礎的應用軟體的架構設計,得到一個功能強、性能好、適宜通信的架構。 -Fielding
這是Fielding在論文中所提到的,對於REST雖說是架構,但如果深入一點,就像是HTTP協議一樣,可以看成一種規則或是協議。我們從一個地點到另一個地點,可以坐汽車、高鐵、飛機等,對於REST就像是其中的一種交通方式,但REST的根本是HTTP協議,也就是說REST是基於HTTP協議的,這點就像坐汽車必須要有公路,坐高鐵必須要有鐵路是一樣的道理,有時候為什麼選用REST,就像我們從南京到徐州,選擇坐高鐵而不選擇坐飛機一樣。
上面這個比喻可能不太恰當,但是思想都是相同的,如果有可能的話可以看下一些哲學方面的書,像莊子的道德經,畢竟編程是哲學或是藝術的另一類體現,又扯遠了。
“Representational State Transfer”我們分解下:
- Representational 表現層:表現層表現什麼,應該呈現資源(Resources),一個圖片、一段文字、一個文件都成為資源,每個資源都用一個URI(統一資源定位符)指向它,表現層就是調用URI把資源呈現出來,而且只是呈現,不做其他操作。舉個例子:有些網址最後的".html"尾碼名是不必要的,因為這個尾碼名錶示格式,屬於"表現層"範疇,而URI應該只代表"資源"的位置。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type欄位指定,這兩個欄位才是對"表現層"的描述。
- State Transfer 狀態轉化:訪問一個網站,就表示客戶端和伺服器發生一次交互行為,在這個過程中,就不發生數據和狀態的轉化,上面說到HTTP協議具有無狀態性,如果客戶端操作伺服器,必須要狀態轉化,這個體現在表現層上,所以叫“表現層狀態轉化”。
通過上面的理解,可以總結下什麼是RESTful架構:
- 每一個URI代表一種資源。
- 客戶端和伺服器之間,傳遞這種資源的某種表現層。
- 客戶端通過四個HTTP動詞(PUT、GET、POST和DELETE),對伺服器端資源進行操作,實現"表現層狀態轉化"。
創建Web API
關於Web API的實現方式,.net提供了一套機制就是ASP.NET MVC API,類似MVC的方式,實現起來很簡單,也不需要你去關註HTTP和RESTful的一些東西,當你去新建項目的時候,一切東西.net都幫你搞定了,是好還是不好?只能呵呵笑過。
下麵我們就一步一步的創建一個ASP.NET MVC API。
1,新建ASP.NET MVC WebMvc的ApiDemo程式,選擇Web API模板類型。
2,創建News模型,雖然創建的是MVC項目,但是我們一般只用到控制器和模型。
1 public class News 2 { 3 /// <summary> 4 /// 新聞ID 5 /// </summary> 6 public int Id { get; set; } 7 /// <summary> 8 /// 新聞標題 9 /// </summary> 10 public string Title { get; set; } 11 /// <summary> 12 /// 新聞內容 13 /// </summary> 14 public string Content { get; set; } 15 /// <summary> 16 /// 新聞作者 17 /// </summary> 18 public string Author { get; set; } 19 /// <summary> 20 /// 發佈新聞時間 21 /// </summary> 22 public DateTime CreateTime { get; set; } 23 }
3,創建數據模擬類NewsRepository,用於查詢數據。
1 public class NewsRepository 2 { 3 public IEnumerable<News> GetAllNews() 4 { 5 News[] news = new News[] 6 { 7 new News { Id = 1, Title="新聞標題1", Content="新聞內容1", Author="xishuai", CreateTime=DateTime.Now }, 8 new News { Id = 2, Title="新聞標題2", Content="新聞內容2", Author="xishuai", CreateTime=DateTime.Now }, 9 new News { Id = 3, Title="新聞標題2", Content="新聞內容3", Author="xishuai", CreateTime=DateTime.Now } 10 }; 11 return news; 12 } 13 }
4,創建NewsController控制器,註意的是:新建控制器的時候,模板選擇“空 API 控制器”,與MVC控制器不同的是,API控制器繼承ApiController基類。在我們新建ApiDemo的MVC項目的時候,自動生成了一個ValuesController API控制器,打開後我們發現,有很多自動生成的方法,請求方式就是我們上面說GET、POST、PUT和DELETE的四種HTTP請求方式,我們這邊做一個Get的,獲取全部新聞或是指定新聞ID獲取,返回結果格式為XML。
1 public class NewsController : ApiController 2 { 3 /// <summary> 4 /// GET獲取全部新聞 5 /// </summary> 6 /// <returns></returns> 7 [HttpGet] 8 public HttpResponseMessage GetAllNews() 9 { 10 var news = new NewsRepository().GetAllNews(); 11 return new HttpResponseMessage() 12 { 13 RequestMessage = Request, 14 Content = new XmlContent(SimpleXmlConverter.ToXmlDocument<News>(news, "NewsRoot")) 15 }; 16 } 17 18 /// <summary> 19 /// GET獲取指定ID新聞 20 /// </summary> 21 /// <param name="ID"></param> 22 /// <returns></returns> 23 [HttpGet] 24 public HttpResponseMessage GetNewsByID(int ID) 25 { 26 var news = new NewsRepository().GetAllNews().Where((p) => p.Id == ID); 27 return new HttpResponseMessage() 28 { 29 RequestMessage = Request, 30 Content = new XmlContent(SimpleXmlConverter.ToXmlDocument<News>(news, "NewsRoot")) 31 }; 32 } 33 }
這裡面主要用到兩個轉化類:XmlContent和SimpleXmlConverter,有關這兩個類的詳細代碼可以下載Demo看下。
- XmlContent:XML格式輸出,參考http://stackoverflow.com/questions/15366096/how-to-return-xml-data-from-a-web-api-method。
- SimpleXmlConverter:實體集合轉化XDocument、XElement、XDocument,參考http://www.cnblogs.com/jasenkin/archive/2012/02/19/simpe_xml_converter.html,裡面的代碼稍微修改了下。
5,路由配置,Web API和MVC的路由配置很相似,主要區別是Web API使用HTTP方法而不是URI路徑來選擇動作,路由文件WebApiConfig,如下:
1 config.Routes.MapHttpRoute( 2 name: "DefaultApi", 3 routeTemplate: "api/{controller}/{action}/{id}", 4 defaults: new { id = RouteParameter.Optional } 5 );
其實寫到這裡創建Web API的代碼就差不多了,當然複雜的操作可以擴充,用.net開發就是這麼簡單,只要完成業務邏輯就行了,呵呵。
調用Web API
調用Web API有很多種方式,js可以調用,就像我們寫MVC請求數據一樣,這種是同一域下調用,Web API也是這種方式就沒什麼意思了,如果使用js調用就必須跨域操作,這邊我們使用HttpClient的方式。
1,新建控制台應用程式-ClientDemo。
2,安裝Web API 客戶端庫,工具-程式包管理器-程式包管理控制平臺,輸入命令:Install-Package Microsoft.AspNet.WebApi.Client
上面新建的ClientDemo項目.net framework版本是4.0,安裝HttpClient的時候報下麵錯誤:
.net framework版本改為4.5就安裝成功了,難道HttpClient只支持4.5以上?查了下MSDN,HttpClient確實只支持.net framework4.5,難道以前程式如果使用HttpClient要把.net framework改成4.5?有點鬱悶。
4,創建HttpClient,先貼下代碼:
1 static void Main(string[] args) 2 { 3 RunAsync().Wait(); 4 } 5 6 static async Task RunAsync() 7 { 8 using (var client = new HttpClient()) 9 { 10 client.BaseAddress = new Uri("http://localhost:8077/"); 11 client.DefaultRequestHeaders.Accept.Clear(); 12 client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); 13 14 string xmlString = await client.GetStringAsync("api/News/GetAllNews"); 15 XmlDocument xmlDoc = new XmlDocument(); 16 xmlDoc.LoadXml(xmlString); 17 XmlNodeList nodeList = xmlDoc.GetElementsByTagName("News"); 18 foreach (XmlNode node in nodeList) 19 { 20 Console.WriteLine("新聞ID:" + node.SelectSingleNode("Id").InnerText); 21 Console.WriteLine("新聞標題:" + node.SelectSingleNode("Title").InnerText); 22 Console.WriteLine("新聞內容:" + node.SelectSingleNode("Content").InnerText); 23 Console.WriteLine("作者:" + node.SelectSingleNode("Author").InnerText); 24 Console.WriteLine("新聞發佈時間:" + node.SelectSingleNode("CreateTime").InnerText); 25 Console.WriteLine("======================"); 26 } 27 Console.ReadKey(); 28 } 29 }
Main 函數調用一個名為RunAsync的非同步方法,然後會阻止,直到RunAsync完成。許多的HttpClient方法是非同步,因為他們執行網路 i/o 操作。MediaTypeWithQualityHeaderValue是定義傳輸格式,如果使用json則為“application/json”,這邊要與Web API格式一致,XML獲取使用的是GetStringAsync方法,然後轉換為XmlDocument,網上找了一種GetStreamAsync方法獲取,通過Stream轉化為字元串,但是轉化後的字元串為空。
上面調用Web API的是獲取全部新聞,如果調用通過新聞ID獲取新聞,GetStringAsync的方法參數只需要改為“api/News/GetNewsByID/新聞ID”就可以了。
5,關於Web API的發佈問題,這個問題花了不少時間,一個一個問題出現及解決,首先我們先不急著調用,先發佈瀏覽,如果瀏覽器瀏覽沒問題的話再調用。Web API的發佈和MVC差不多,需要註意下麵幾點:
- 文件許可權別忘了配置,要配置every one。
- 應用程式池選擇“ASP.NET v4.0 DefaultAppPool”。
- 選擇應用程式池之後要配置“ISAPI 和 CGI 限制”的4.0版本設置為允許,要不然出現“由於 Web 伺服器上的“ISAPI 和 CGI 限制”列表設置,無法提供您請求的頁面。”的錯誤。
- “Newtonsoft.Json”文件引用。
- “An error has occurred.Multiple actions were found that match the request”,這個錯誤信息是Web API的路由沒有配置好,要檢查下。
- “Response status code does not indicate success: 404 (Not Found)”,這個錯誤信息也是路由問題。
- “Response status code does not indicate success: 500 (Internal Server Error).”,這個是伺服器問題,也就是Web API的代碼問題。
運行截圖及Demo下載
Web API瀏覽器請求截圖:
Web API發佈運行截圖:
客戶端調用運行截圖:
Demo下載地址:http://pan.baidu.com/s/1gdopRub
HTTP請求的四種方法(PUT、GET、POST和DELETE)本篇只是講到GET,HTTP的數據傳輸格式(json、xml等)也只是說了XML,但都是大同小異,舉一反三可得。
如果你覺得本篇文章對你有所幫助,請點擊右下部“推薦”,^_^