ASP.NET Web API(MVC API)
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class News
{
/// <summary>
/// 新聞ID
/// </summary>
public int Id { get ; set ; }
/// <summary>
/// 新聞標題
/// </summary>
public string Title { get ; set ; }
/// <summary>
/// 新聞內容
/// </summary>
public string Content { get ; set ; }
/// <summary>
/// 新聞作者
/// </summary>
public string Author { get ; set ; }
/// <summary>
/// 釋出新聞時間
/// </summary>
public DateTime CreateTime { get ; set ; }
}
|
3,建立資料模擬類NewsRepository,用於查詢資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class NewsRepository
{
public IEnumerable<News> GetAllNews()
{
News[] news = new News[]
{
new News { Id = 1, Title= "新聞標題1" , Content= "新聞內容1" , Author= "xishuai" , CreateTime=DateTime.Now },
new News { Id = 2, Title= "新聞標題2" , Content= "新聞內容2" , Author= "xishuai" , CreateTime=DateTime.Now },
new News { Id = 3, Title= "新聞標題2" , Content= "新聞內容3" , Author= "xishuai" , CreateTime=DateTime.Now }
};
return news;
}
}
|
4,建立NewsController控制器,注意的是:新建控制器的時候,模板選擇“空 API 控制器”,與MVC控制器不同的是,API控制器繼承ApiController基類。在我們新建ApiDemo的MVC專案的時候,自動生成了一個ValuesController API控制器,開啟後我們發現,有很多自動生成的方法,請求方式就是我們上面說GET、POST、PUT和DELETE的四種HTTP請求方式,我們這邊做一個Get的,獲取全部新聞或是指定新聞ID獲取,返回結果格式為XML。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class NewsController : ApiController
{
/// <summary>
/// GET獲取全部新聞
/// </summary>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage GetAllNews()
{
var news = new NewsRepository().GetAllNews();
return new HttpResponseMessage()
{
RequestMessage = Request,
Content = new XmlContent(SimpleXmlConverter.ToXmlDocument<News>(news, "NewsRoot" ))
};
}
/// <summary>
/// GET獲取指定ID新聞
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage GetNewsByID( int ID)
{
var news = new NewsRepository().GetAllNews().Where((p) => p.Id == ID);
return new HttpResponseMessage()
{
RequestMessage = Request,
Content = new XmlContent(SimpleXmlConverter.ToXmlDocument<News>(news, "NewsRoot" ))
};
}
}
|
這裡面主要用到兩個轉化類: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 2 3 4 5 6 |
config.Routes.MapHttpRoute
(
name: "DefaultApi" ,
routeTemplate: "api/{controller}/{action}/{id}" ,
defaults: new { id = RouteParameter.Optional }
);
|
其實寫到這裡建立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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
static void Main( string [] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using ( var client = new HttpClient())
{
client.BaseAddress = new Uri( "http://localhost:8077/" );
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/xml" ));
string xmlString = await client.GetStringAsync( "api/News/GetAllNews" );
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
XmlNodeList nodeList = xmlDoc.GetElementsByTagName( "News" );
foreach (XmlNode node in nodeList)
{
Console.WriteLine( "新聞ID:" + node.SelectSingleNode( "Id" ).InnerText);
Console.WriteLine( "新聞標題:" + node.SelectSingleNode( "Title" ).InnerText);
Console.WriteLine( "新聞內容:" + node.SelectSingleNode( "Content" ).InnerText);
Console.WriteLine( "作者:" + node.SelectSingleNode( "Author" ).InnerText);
Console.WriteLine( "新聞釋出時間:" + node.SelectSingleNode( "CreateTime" ).InnerText);
Console.WriteLine( "======================" );
}
Console.ReadKey();
}
}
|
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,但都是大同小異,舉一反三可得。