來(lái)源:懶得安分
鏈接:http://www.cnblogs.com/landeanfen/p/5337072.html
前言:還記得剛使用WebApi那會(huì )兒,被它的傳參機制折騰了好久,查閱了半天資料。如今,使用WebApi也有段時(shí)間了,今天就記錄下API接口傳參的一些方式方法,算是一個(gè)筆記,也希望能幫初學(xué)者少走彎路。本篇針對初初使用WebApi的同學(xué)們,比較基礎,有興趣的且看看。
本篇打算通過(guò)get、post、put、delete四種請求方式分別談?wù)劵A類(lèi)型(包括int/string/datetime等)、實(shí)體、數組等類(lèi)型的參數如何傳遞。
對于取數據,我們使用最多的應該就是get請求了吧。下面通過(guò)幾個(gè)示例看看我們的get請求參數傳遞。
[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);
}
}
});
參數截圖效果
這是get請求最基礎的參數傳遞方式,沒(méi)什么特別好說(shuō)的。
如果我們在get請求時(shí)想將實(shí)體對象做參數直接傳遞到后臺,是否可行呢?我們來(lái)看看。
public class TB_CHARGING
{
///
/// 主鍵Id
///
public string ID { get; set; }
///
/// 充電設備名稱(chēng)
///
public string NAME { get; set; }
///
/// 充電設備描述
///
public string DES { get; set; }
///
/// 創(chuàng )建時(shí)間
///
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);
}
}
});
測試結果
由上圖可知,在get請求時(shí),我們直接將json對象當做實(shí)體傳遞后臺,后臺是接收不到的。這是為什么呢?我們來(lái)看看對應的http請求
原來(lái),get請求的時(shí)候,默認是將參數全部放到了url里面直接以string的形式傳遞的,后臺自然接不到了。
原因分析:還記得有面試題問(wèn)過(guò)get和post請求的區別嗎?其中有一個(gè)區別就是get請求的數據會(huì )附在URL之后(就是把數據放置在HTTP協(xié)議頭中),而post請求則是放在http協(xié)議包的包體中。
根據園友們的提議,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;
}
得到結果:
如果你不想使用[FromUri]這些在參數里面加特性的這種“怪異”寫(xiě)法,也可以采用先序列化,再在后臺反序列的方式。
$.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;
}
這樣在后臺得到我們序列化過(guò)的對象,再通過(guò)反序列化就能得到對象。
在url里面我們可以看到它自動(dòng)給對象加了一個(gè)編碼:

一般get請求不建議將數組作為參數,因為我們知道get請求傳遞參數的大小是有限制的,最大1024字節,數組里面內容較多時(shí),將其作為參數傳遞可能會(huì )發(fā)生參數超限丟失的情況。
為什么會(huì )說(shuō)get請求“怪異”呢?我們先來(lái)看看下面的兩種寫(xiě)法對比。
$.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);
}
}
});
這是標準寫(xiě)法,后臺加[HttpGet],參數正常得到:

為了對比,我將[HttpGet]去掉,然后再調用
//[HttpGet]
public string GetByModel(string strQuery)
{
TB_CHARGING oData = Newtonsoft.Json.JsonConvert.DeserializeObject(strQuery);
return 'ChargingData' + oData.ID;
}

貌似沒(méi)有任何問(wèn)題!有人就想,那是否所有的get請求都可以省略掉[HttpGet]這個(gè)標注呢。我們試試便知。
我們把之前的方法名由GetByModel改成FindByModel,這個(gè)再正常不過(guò)了,很多人查詢(xún)就不想用Get開(kāi)頭,還有直接用Query開(kāi)頭的。這個(gè)有什么關(guān)系嗎?有沒(méi)有關(guān)系,我們以事實(shí)說(shuō)話(huà)。
$.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;
}

貌似又可行,沒(méi)有任何問(wèn)題啊。根據上面的推論,我們去掉[HttpGet]也是可行的,好,我們注釋掉[HttpGet],運行起來(lái)試試。

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

呵呵,這就奇怪了,就改了個(gè)方法名,至于這樣么?還真至于!
博主的理解是:方法名以Get開(kāi)頭,WebApi會(huì )自動(dòng)默認這個(gè)請求就是get請求,而如果你以其他名稱(chēng)開(kāi)頭而又不標注方法的請求方式,那么這個(gè)時(shí)候服務(wù)器雖然找到了這個(gè)方法,但是由于請求方式不確定,所以直接返回給你405——方法不被允許的錯誤。
最后結論:所有的WebApi方法最好是加上請求的方式([HttpGet]/[HttpPost]/[HttpPut]/[HttpDelete]),不要偷懶,這樣既能防止類(lèi)似的錯誤,也有利于方法的維護,別人一看就知道這個(gè)方法是什么請求。
這也就是為什么很多人在園子里面問(wèn)道為什么方法名不加[HttpGet]就調用不到的原因!
在WebApi的RESETful風(fēng)格里面,API服務(wù)的增刪改查,分別對應著(zhù)http的post/delete/put/get請求。我們下面就來(lái)說(shuō)說(shuō)post請求參數的傳遞方式。
post請求的基礎類(lèi)型的參數和get請求有點(diǎn)不一樣,我們知道get請求的參數是通過(guò)url來(lái)傳遞的,而post請求則是通過(guò)http的請求體中傳過(guò)來(lái)的,WebApi的post請求也需要從http的請求體里面去取參數。
$.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;
}
這是一種看上去非常正確的寫(xiě)法,可是實(shí)際情況是:

$.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;
}
這是一種另許多人頭痛的寫(xiě)法,但是沒(méi)辦法,這樣確實(shí)能得到我們的結果:

我們一般的通過(guò)url取參數的機制是鍵值對,即某一個(gè)key等于某一個(gè)value,而這里的FromBody和我們一般通過(guò)url取參數的機制則不同,它的機制是=value,沒(méi)有key的概念,并且如果你寫(xiě)了key(比如你的ajax參數寫(xiě)的{NAME:”Jim”}),后臺反而得到的NAME等于null。不信你可以試試。
上面講的都是傳遞一個(gè)基礎類(lèi)型參數的情況,那么如果我們需要傳遞多個(gè)基礎類(lèi)型呢?按照上面的推論,是否可以([FromBody]string NAME, [FromBody]string DES)這樣寫(xiě)呢。試試便知。
$.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;
}


這說(shuō)明我們沒(méi)辦法通過(guò)多個(gè)[FromBody]里面取值,此法失敗。
既然上面的辦法行不通,那我們如何傳遞多個(gè)基礎類(lèi)型的數據呢?很多的解決辦法是新建一個(gè)類(lèi)去包含傳遞的參數,博主覺(jué)得這樣不夠靈活,因為如果我們前后臺每次傳遞多個(gè)參數的post請求都去新建一個(gè)類(lèi)的話(huà),我們系統到時(shí)候會(huì )有多少個(gè)這種參數類(lèi)?維護起來(lái)那是相當的麻煩的一件事!所以博主覺(jué)得使用dynamic是一個(gè)很不錯的選擇。我們來(lái)試試。
$.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;
}

通過(guò)dynamic動(dòng)態(tài)類(lèi)型能順利得到多個(gè)參數,省掉了[FromBody]這個(gè)累贅,并且ajax參數的傳遞不用使用”無(wú)厘頭”的{“”:”value”}這種寫(xiě)法,有沒(méi)有一種小清新的感覺(jué)~~有一點(diǎn)需要注意的是這里在ajax的請求里面需要加上參數類(lèi)型為Json,即 contentType: ‘a(chǎn)pplication/json’, 這個(gè)屬性。
通過(guò)上文post請求基礎類(lèi)型參數的傳遞,我們了解到了dynamic的方便之處,為了避免[FromBody]這個(gè)累贅和{“”:”value”}這種”無(wú)厘頭”的寫(xiě)法。博主推薦所有基礎類(lèi)型使用dynamic來(lái)傳遞,方便解決了基礎類(lèi)型一個(gè)或多個(gè)參數的傳遞,示例如上文。如果園友們有更好的辦法,歡迎討論。
上面我們通過(guò)dynamic類(lèi)型解決了post請求基礎類(lèi)型數據的傳遞問(wèn)題,那么當我們需要傳遞一個(gè)實(shí)體作為參數該怎么解決呢?我們來(lái)看下面的代碼便知:
$.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;
}
得到結果

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

為了弄清楚原因,博主查了下http的Content-Type的類(lèi)型??吹饺缦抡f(shuō)明:
application/x-www-form-urlencoded :中默認的encType,form表單數據被編碼為key/value格式發(fā)送到服務(wù)器(表單默認的提交數據的格式);
application/json : JSON數據格式
也就是說(shuō)post請求默認是將表單里面的數據的key/value形式發(fā)送到服務(wù),而我們的服務(wù)器只需要有對應的key/value屬性值的對象就可以接收到。而如果使用application/json,則表示將前端的數據以序列化過(guò)的json傳遞到后端,后端要把它變成實(shí)體對象,還需要一個(gè)反序列化的過(guò)程。按照這個(gè)邏輯,那我們如果指定contentType為application/json,然后傳遞序列化過(guò)的對象應該也是可以的啊。博主好奇心重,還是打算一試到底,于是就有了下面的代碼:
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;
}
得到結果:

嘗試成功,也就是說(shuō),兩種寫(xiě)法都是可行的。如果你指定了contentType為application/json,則必須要傳遞序列化過(guò)的對象;如果使用post請求的默認參數類(lèi)型,則前端直接傳遞json類(lèi)型的對象即可。
有些時(shí)候,我們需要將基礎類(lèi)型和實(shí)體一起傳遞到后臺,這個(gè)時(shí)候,我們神奇的dynamic又派上用場(chǎ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({ 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;
}
得到結果:

原理也不用多說(shuō),同上。
WebAPI系列文章:
聯(lián)系客服