前言:還記得剛使用WebApi那會兒,被它的傳參機(jī)制折騰了好久,查閱了半天資料。如今,使用WebApi也有段時間了,今天就記錄下API接口傳參的一些方式方法,算是一個筆記,也希望能幫初學(xué)者少走彎路。本篇針對初初使用WebApi的同學(xué)們,比較基礎(chǔ),有興趣的且看看。
本篇打算通過get、post、put、delete四種請求方式分別談?wù)劵A(chǔ)類型(包括int/string/datetime等)、實體、數(shù)組等類型的參數(shù)如何傳遞。
對于取數(shù)據(jù),我們使用最多的應(yīng)該就是get請求了吧。下面通過幾個示例看看我們的get請求參數(shù)傳遞。
[HttpGet]public string GetAllChargingData(int id, string name){return "ChargingData" + id;}$.Ajax({type: "get",url: "http://localhost:27221/API/Charging/GetAllChargingData",data: { id: 1, name: "Jim", bir: "1988-09-11"},sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});參數(shù)截圖效果

這是get請求最基礎(chǔ)的參數(shù)傳遞方式,沒什么特別好說的。
2、實體作為參數(shù)
如果我們在get請求時想將實體對象做參數(shù)直接傳遞到后臺,是否可行呢?我們來看看。
    public class TB_CHARGING{/// /// 主鍵Id/// public string ID { get; set; }/// /// 充電設(shè)備名稱/// public string NAME { get; set; }/// /// 充電設(shè)備描述/// public string DES { get; set; }/// /// 創(chuàng)建時間/// public DateTime CREATETIME { get; set; }}[HttpGet]public string GetByModel(TB_CHARGING oData){return "ChargingData" + oData.ID;}$.ajax({type: "get",url: "http://localhost:27221/API/Charging/GetByModel",contentType: "application/json",data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});測試結(jié)果

由上圖可知,在get請求時,我們直接將json對象當(dāng)做實體傳遞后臺,后臺是接收不到的。這是為什么呢?我們來看看對應(yīng)的http請求

原來,get請求的時候,默認(rèn)是將參數(shù)全部放到了url里面直接以string的形式傳遞的,后臺自然接不到了。
原因分析:還記得有面試題問過get和post請求的區(qū)別嗎?其中有一個區(qū)別就是get請求的數(shù)據(jù)會附在URL之后(就是把數(shù)據(jù)放置在HTTP協(xié)議頭中),而post請求則是放在http協(xié)議包的包體中。
根據(jù)園友們的提議,Get請求的時候可以在參數(shù)里面加上[FromUri]即可直接得到對象。還是貼上代碼:
 var postdata={ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };$.ajax({type: "get",url: "http://localhost:27221/API/Charging/GetAllChargingData",data: postdata,sucCESs: function (data, status) { }});  [HttpGet]public string    GetAllChargingData([FromUri]TB_CHARGING obj){return "ChargingData" + obj.ID;}得到結(jié)果:

如果你不想使用[FromUri]這些在參數(shù)里面加特性的這種“怪異”寫法,也可以采用先序列化,再在后臺反序列的方式。
 $.ajax({type: "get",url: "http://localhost:27221/API/Charging/GetByModel",contentType: "application/json",data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});    [HttpGet]public string GetByModel(string strQuery){TB_CHARGING oData=Newtonsoft.Json.JsonConvert.DeserializeObject(strQuery);return "ChargingData" + oData.ID;}
這樣在后臺得到我們序列化過的對象,再通過反序列化就能得到對象。
在url里面我們可以看到它自動給對象加了一個編碼:

3、數(shù)組作為參數(shù)
一般get請求不建議將數(shù)組作為參數(shù),因為我們知道get請求傳遞參數(shù)的大小是有限制的,最大1024字節(jié),數(shù)組里面內(nèi)容較多時,將其作為參數(shù)傳遞可能會發(fā)生參數(shù)超限丟失的情況。
4、“怪異”的get請求
為什么會說get請求“怪異”呢?我們先來看看下面的兩種寫法對比。
(1)WebApi的方法名稱以get開頭
$.ajax({type: "get",url: "http://localhost:27221/API/Charging/GetByModel",contentType: "application/json",data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});   [HttpGet]public string GetByModel(string strQuery){TB_CHARGING oData=Newtonsoft.Json.JsonConvert.DeserializeObject(strQuery);return "ChargingData" + oData.ID;}這是標(biāo)準(zhǔn)寫法,后臺加[HttpGet],參數(shù)正常得到:

為了對比,我將[HttpGet]去掉,然后再調(diào)用
   //[HttpGet]public string GetByModel(string strQuery){TB_CHARGING oData=Newtonsoft.Json.JsonConvert.DeserializeObject(strQuery);return "ChargingData" + oData.ID;}
貌似沒有任何問題!有人就想,那是否所有的get請求都可以省略掉[HttpGet]這個標(biāo)注呢。我們試試便知。
(2)WebApi的方法名稱不以get開頭
我們把之前的方法名由GetByModel改成FindByModel,這個再正常不過了,很多人查詢就不想用Get開頭,還有直接用Query開頭的。這個有什么關(guān)系嗎?有沒有關(guān)系,我們以事實說話。
 $.ajax({type: "get",url: "http://localhost:27221/API/Charging/FindByModel",contentType: "application/json",data: { strQuery: JSON.stringify({ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }) },sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});     [HttpGet]public string FindByModel(string strQuery){TB_CHARGING oData=Newtonsoft.Json.JsonConvert.DeserializeObject(strQuery);return "ChargingData" + oData.ID;}
貌似又可行,沒有任何問題啊。根據(jù)上面的推論,我們?nèi)サ鬧HttpGet]也是可行的,好,我們注釋掉[HttpGet],運行起來試試。

結(jié)果是不進(jìn)斷點,有些人不信,我們在瀏覽器里面看看http請求:

呵呵,這就奇怪了,就改了個方法名,至于這樣么?還真至于!
博主的理解是:方法名以Get開頭,WebApi會自動默認(rèn)這個請求就是get請求,而如果你以其他名稱開頭而又不標(biāo)注方法的請求方式,那么這個時候服務(wù)器雖然找到了這個方法,但是由于請求方式不確定,所以直接返回給你405——方法不被允許的錯誤。
最后結(jié)論:所有的WebApi方法最好是加上請求的方式([HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]),不要偷懶,這樣既能防止類似的錯誤,也有利于方法的維護(hù),別人一看就知道這個方法是什么請求。
這也就是為什么很多人在園子里面問道為什么方法名不加[HttpGet]就調(diào)用不到的原因!
二、post請求
在WebApi的RESETful風(fēng)格里面,API服務(wù)的增刪改查,分別對應(yīng)著http的post/delete/put/get請求。我們下面就來說說post請求參數(shù)的傳遞方式。
1、基礎(chǔ)類型參數(shù)
post請求的基礎(chǔ)類型的參數(shù)和get請求有點不一樣,我們知道get請求的參數(shù)是通過url來傳遞的,而post請求則是通過http的請求體中傳過來的,WebApi的post請求也需要從http的請求體里面去取參數(shù)。
(1)錯誤的寫法
 $.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",data: { NAME: "Jim" },sucCESs: function (data, status) {if (status=="sucCESs") {$("#div_test").html(data);}}});   [HttpPost]public bool SaveData(string NAME){return true;}這是一種看上去非常正確的寫法,可是實際情況是:

(2)正確的用法
$.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",data: { "": "Jim" },sucCESs: function (data, status) {}});     [HttpPost]public bool SaveData([FromBody]string NAME){return true;}這是一種另許多人頭痛的寫法,但是沒辦法,這樣確實能得到我們的結(jié)果:

我們一般的通過url取參數(shù)的機(jī)制是鍵值對,即某一個key等于某一個value,而這里的FromBody和我們一般通過url取參數(shù)的機(jī)制則不同,它的機(jī)制是=value,沒有key的概念,并且如果你寫了key(比如你的ajax參數(shù)寫的{NAME:Jim}),后臺反而得到的NAME等于null。不信你可以試試。
上面講的都是傳遞一個基礎(chǔ)類型參數(shù)的情況,那么如果我們需要傳遞多個基礎(chǔ)類型呢?按照上面的推論,是否可以([FromBody]string NAME, [FromBody]string DES)這樣寫呢。試試便知。
(1)錯誤寫法
 $.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",data: { "": "Jim","":"備注" },sucCESs: function (data, status) {}});  [HttpPost]public bool SaveData([FromBody]string NAME, [FromBody] string DES){return true;}到結(jié)果


這說明我們沒辦法通過多個[FromBody]里面取值,此法失敗。
(2)正確用法
既然上面的辦法行不通,那我們?nèi)绾蝹鬟f多個基礎(chǔ)類型的數(shù)據(jù)呢?很多的解決辦法是新建一個類去包含傳遞的參數(shù),博主覺得這樣不夠靈活,因為如果我們前后臺每次傳遞多個參數(shù)的post請求都去新建一個類的話,我們系統(tǒng)到時候會有多少個這種參數(shù)類?維護(hù)起來那是相當(dāng)?shù)穆闊┑囊患?!所以博主覺得使用dynamic是一個很不錯的選擇。我們來試試。
 $.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",contentType: application/json,data: JSON.stringify({ NAME: "Jim",DES:"備注" }),sucCESs: function (data, status) {}});    [HttpPost]public object SaveData(dynamic obj){var strName=Convert.ToString(obj.NAME);return strName;}
通過dynamic動態(tài)類型能順利得到多個參數(shù),省掉了[FromBody]這個累贅,并且ajax參數(shù)的傳遞不用使用無厘頭的{:value}這種寫法,有沒有一種小清新的感覺~~有一點需要注意的是這里在ajax的請求里面需要加上參數(shù)類型為Json,即 contentType: application/json, 這個屬性。
(3)推薦用法
通過上文post請求基礎(chǔ)類型參數(shù)的傳遞,我們了解到了dynamic的方便之處,為了避免[FromBody]這個累贅和{:value}這種無厘頭的寫法。博主推薦所有基礎(chǔ)類型使用dynamic來傳遞,方便解決了基礎(chǔ)類型一個或多個參數(shù)的傳遞,示例如上文。如果園友們有更好的辦法,歡迎討論。
2、實體作為參數(shù)
(1)單個實體作為參數(shù)
上面我們通過dynamic類型解決了post請求基礎(chǔ)類型數(shù)據(jù)的傳遞問題,那么當(dāng)我們需要傳遞一個實體作為參數(shù)該怎么解決呢?我們來看下面的代碼便知:
 $.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",data: { ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },sucCESs: function (data, status) {}});   [HttpPost]public bool SaveData(TB_CHARGING oData){return true;}得到結(jié)果

原理解釋:使用實體作為參數(shù)的時候,前端直接傳遞普通json,后臺直接使用對應(yīng)的類型去接收即可,不用FromBody。但是這里需要注意的一點就是,這里不能指定contentType為appplication/json,否則,參數(shù)無法傳遞到后臺。我們來看看它默認(rèn)的contentType是什么:

為了弄清楚原因,博主查了下http的Content-Type的類型??吹饺缦抡f明:
application/x-www-form-urlencoded :
application/json    : JSON數(shù)據(jù)格式也就是說post請求默認(rèn)是將表單里面的數(shù)據(jù)的key/value形式發(fā)送到服務(wù),而我們的服務(wù)器只需要有對應(yīng)的key/value屬性值的對象就可以接收到。而如果使用application/json,則表示將前端的數(shù)據(jù)以序列化過的json傳遞到后端,后端要把它變成實體對象,還需要一個反序列化的過程。按照這個邏輯,那我們?nèi)绻付╟ontentType為application/json,然后傳遞序列化過的對象應(yīng)該也是可以的啊。博主好奇心重,還是打算一試到底,于是就有了下面的代碼:
    var postdata={ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };$.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",contentType: application/json,data: JSON.stringify(postdata),sucCESs: function (data, status) {}});     [HttpPost]public bool SaveData(TB_CHARGING lstCharging){return true;}得到結(jié)果:

嘗試成功,也就是說,兩種寫法都是可行的。如果你指定了contentType為application/json,則必須要傳遞序列化過的對象;如果使用post請求的默認(rèn)參數(shù)類型,則前端直接傳遞json類型的對象即可。
(2)實體和基礎(chǔ)類型一起作為參數(shù)傳遞
有些時候,我們需要將基礎(chǔ)類型和實體一起傳遞到后臺,這個時候,我們神奇的dynamic又派上用場了。
var postdata={ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" };$.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",contentType: application/json,data: JSON.stringify({ NAME:"Lilei", Charging:postdata }),sucCESs: function (data, status) {}});     [HttpPost]public object SaveData(dynamic obj){var strName=Convert.ToString(obj.NAME);var oCharging=Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(obj.Charging));return strName;}得到結(jié)果:

原理也不用多說,同上。
3、數(shù)組作為參數(shù)
(1)基礎(chǔ)類型數(shù)組
 var arr=["1", "2", "3", "4"];$.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",contentType: application/json,data: JSON.stringify(arr),sucCESs: function (data, status) { }});    [HttpPost]public bool SaveData(string[] ids){return true;}得到結(jié)果:

(2)實體集合
 var arr=[{ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },{ ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },{ ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }];$.ajax({type: "post",url: "http://localhost:27221/API/Charging/SaveData",contentType: application/json,data: JSON.stringify(arr),sucCESs: function (data, status) {}});    [HttpPost]public bool SaveData(List lstCharging){return true;}得到結(jié)果:

4、后臺發(fā)送請求參數(shù)的傳遞
上面寫了那么多,都是通過前端的ajax請求去做的,我們知道,如果調(diào)用方不是web項目,比如Android客戶端,可能需要從后臺發(fā)送http請求來調(diào)用我們的接口方法,如果我們通過后臺去發(fā)送請求是否也是可行的呢?我們以實體對象作為參數(shù)來傳遞寫寫代碼試一把。
public void TestReques()    {     //請求路徑string url="http://localhost:27221/API/Charging/SaveData";//定義request并設(shè)置request的路徑WebRequest request=WebRequest.Create(url);request.Method="post";//初始化request參數(shù)string postData="{ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" }";//設(shè)置參數(shù)的編碼格式,解決中文亂碼byte[] byteArray=Encoding.UTF8.GetBytes(postData);//設(shè)置request的MIME類型及內(nèi)容長度request.ContentType="application/json";request.ContentLength=byteArray.Length;//打開request字符流Stream dataStream=request.GetRequestStream();dataStream.Write(byteArray, 0, byteArray.Length);dataStream.Close();//定義response為前面的request響應(yīng)WebResponse response=request.GetResponse();//獲取相應(yīng)的狀態(tài)代碼Console.WriteLine(((HttpWebResponse)response).StatusDescription);//定義response字符流dataStream=response.GetResponseStream();StreamReader Reader=new StreamReader(dataStream);string responseFromServer=Reader.ReadToEnd();//讀取所有Console.WriteLine(responseFromServer);    }當(dāng)代碼運行到request.GetResponse()這一句的時候,API里面進(jìn)入斷點

嘗試成功。
三、put請求
WebApi里面put請求一般用于對象的更新。它和用法和post請求基本相同。同樣支持[FromBody],同樣可以使用dynamic。
1、基礎(chǔ)類型參數(shù)
 $.ajax({type: "put",url: "http://localhost:27221/API/Charging/Update",contentType: application/json,data: JSON.stringify({ ID: "1" }),sucCESs: function (data, status) {}});   [HttpPut]public bool Update(dynamic obj ){return true;}
2、實體作為參數(shù)
和post請求相同。
3、數(shù)組作為參數(shù)
和post請求相同。
四、delete請求
顧名思義,delete請求肯定是用于刪除操作的。參數(shù)傳遞機(jī)制和post也是基本相同。下面簡單給出一個例子,其他情況參考post請求。
var arr=[{ ID: "1", NAME: "Jim", CREATETIME: "1988-09-11" },{ ID: "2", NAME: "Lilei", CREATETIME: "1990-12-11" },{ ID: "3", NAME: "Lucy", CREATETIME: "1986-01-10" }];$.ajax({type: "delete",url: "http://localhost:27221/API/Charging/OptDelete",contentType: application/json,data: JSON.stringify(arr),sucCESs: function (data, status) {}});    [HttpDelete]public bool OptDelete(List lstChargin){return true;}
五、總結(jié)
以上比較詳細(xì)的總結(jié)了WebApi各種請求的各種參數(shù)傳遞。每種情況都是博主實際代碼測試過的,內(nèi)容不難,但如果剛接觸這么些東西還是需要一點時間去熟悉的,在此做個總結(jié),希望能幫到剛剛接觸WebApi的園友們。
WebAPI系列文章:
《WebApi身份認(rèn)證解決方案(1):Basic基礎(chǔ)認(rèn)證》《WebApi接口測試工具(2):WebApiTestClient》《WebApi 跨域問題解決方案(3):CORS》
新聞熱點
疑難解答