概述:
ASP.NET Web API 的好用使用過的都知道,沒有複雜的配置檔案,一個簡單的ApiController加上需要的Action就能工作。呼叫API過程中引數的傳遞是必須的,本節就來談談API使用過程中引數的傳遞方式。
各種引數傳遞方式的實現:
ASP.NET Web API引數有兩種傳遞方式,一種是請求時攜帶QueryString,Action中沒有表中標註FromUri和FromBody屬性且沒有預設值的引數,Request請求時必需一QueryString的方式攜帶引數?A=&B=&C=1,及時沒有值,也得傳遞,否則報404錯誤。
API 開發中的FromUriAttribute屬性,主要是用來在GET請求中傳遞複雜物件,並且每個API可以含有多個此型別的引數,但每個複雜物件中的屬性名不能相同,否則無法正確傳值,除非兩個引數的相同屬性的屬性值相同,不過此種方式傳遞有一定的侷限性,就是url長度限制,變相的限定此種方式的引數資料量的大小。
另外一種傳遞方式就是請求Request的傳送內容攜帶資料,對應API開發中的FromBodyAttribute屬性,此種方式主要應對POST請求引數的傳遞,可以傳遞複雜的型別,包括陣列,list等,但是每個API有且僅有一個這樣的引數,如果有多個,就會報無法將多個引數繫結到請求的內容
不多上,下面上程式碼,一種一種講解:
1. 簡單型別傳遞
簡單型別包括 int(decimal,long,float)、string(char)、bool、datetime、guid,主要就這幾種
模板: public TResult nameOfFunction([FromUrl]int i, [FromUrl]string s, ……)
=> public TResult nameOfFunction(int i, string s, ……)
但模板: public TResult nameOfFunction([FromUrl]ClassA a, [FromUrl]Class b, ……)
≠> public TResult nameOfFunction(ClassA a, ClassB b, ……)
1 [HttpGet] 2 public ResultData TestParameter(int i, string s, bool b, DateTime t, Guid g) 3 { 4 try 5 { 6 var data = new { i = i, s = s, b = b, t = t, g = g}; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 } 15 16 //TestParameter 17 function TestParameter() { 18 var v = { i: 1, b: false, t: "2016-07-06", g: "E816F0B7-2FB7-47D9-84ED-119F58C9BEC5", s: "test"}; 19 $.ajax({ 20 type: "get", 21 url: host + "/mobileapi/test/TestParameter", 22 dataType: "text", 23 data: v, 24 success: function (data) { 25 alert(data); 26 }, 27 error: function (x, y, z) { 28 alert("報錯無語"); 29 } 30 }); 31 }
結果如下:
注意:
1. GET 型別請求的API不能含有[FromBody]屬性的引數,雖然不會報錯,但永遠沒為null,如果GET請求需要傳遞複雜引數,可以用FromUri屬性修飾引數
2. API引數沒有預設值的情況下,請求的引數名稱必需與API引數名稱保持一致,但不區分大小寫,且能對應上的引數個數一定相等,否則會報404錯誤。
詳細列舉如下:
a. 引數個數相等,但對應的引數少個 g,所有找不到對應API,報404錯誤
1 function TestParameter() { 2 var v = { i: 1, b: false, t: "2016-07-06", s: "test", a: "會報404錯誤" }; 3 $.ajax({ 4 type: "get", 5 url: host + "/mobileapi/test/TestParameter", 6 dataType: "text", 7 data: v, 8 success: function (data) { 9 alert(data); 10 }, 11 error: function (x, y, z) { 12 alert("報錯無語"); 13 } 14 }); 15 }
b. 引數個數不相等,但是能對應上的引數個數相等,不會報404錯誤
1 //TestParameter 2 function TestParameter() { 3 var v = { i: 1, b: false, t: "2016-07-06", s: "test", g: "E816F0B7-2FB7-47D9-84ED-119F58C9BEC5", a: "不會報404錯誤" }; 4 $.ajax({ 5 type: "get", 6 url: host + "/mobileapi/test/TestParameter", 7 dataType: "text", 8 data: v, 9 success: function (data) { 10 alert(data); 11 }, 12 error: function (x, y, z) { 13 alert("報錯無語"); 14 } 15 }); 16 }
如何調整使上述幾種方式也能找到正確的API的呢?這就需要.NET Framework的預設引數功能,API的調整如下:
1 [HttpGet] 2 public ResultData TestParameter(int i, string s, bool b, DateTime t, Guid? g = null) 3 { 4 try 5 { 6 var data = new { i = i, s = s, b = b, t = t, g = g }; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 }
以上程式碼最後一個引數g有個預設值null,這樣get請求的時候可以沒有g引數也能請求通過,不會報404錯誤。另外,值型別的引數最好定義為nullable形式(簡寫可以?標註),這樣的引數賦值不正確的時候也不會報異常錯誤,只是引數值為null。特別是日期類,如果不是nullable型別,不傳值和傳錯值都會報異常
上面例子中的t引數如果為 TestParameter?i=1&b=false&t=2016-37-06&s=test&g=E816F0B7-2FB7-47D9-84ED-119F58C9BEC5或者
http://192.168.1.135:1507/mobileapi/test/TestParameter?i=1&b=false&t=&s=test&g=E816F0B7-2FB7-47D9-84ED-119F58C9BEC5,都會報錯,如果定義為nullable型別就一切正常: public ResultData TestParameter(int i, string s, bool b, DateTime? t, Guid? g = null)。
2. 複雜型別傳遞
GET請求中複雜型別的傳遞需要FormUriAttribute屬性配合,並且每個API可以有多個FromUri標示的引數,也可以在post請求中使用此屬性標註的引數。
1 [HttpGet]
2 public ResultData TestParameter2([FromUri]List<int> ids, [FromUri]User a)
3 {
4 try
5 {
6 var data = new { ids=ids, name=a.name };
7 return new ResultData(data);
8 }
9 catch (Exception ex)
10 {
11 throw ex;
12 //return new ResultData(ResultType.SystemException, ex.Message);
13 }
14 }
測試程式碼:
1 function TestParameter2() {
2 var v = { ids: [1, 2, 3], name='test', age=4, weight=100 };
3 $.ajax({
4 type: "get",
5 url: host + "/mobileapi/test/TestParameter2",
6 dataType: "text",
7 data: { "": v },
8 beforeSend: function (request) {
9 request.setRequestHeader("token", $("#token").val());
10 },
11 success: function (data) {
12 alert(data);
13 },
14 error: function (x, y, z) {
15 alert("報錯無語");
16 }
17 });
18 }
測試結果:GET請求中可以利用FromUri屬性傳遞複雜型別
POST請求中複雜型別的傳遞需要FormBodyAttribute屬性配合,並且每個API有且僅有一個FromBody標示的引數,並且只能在post請求中使用。
1 [HttpPost] 2 public ResultData TestParameter2([FromBody]List<int> ids, [FromBody]List<int> ids2) 3 { 4 try 5 { 6 var data = new { ids=ids, ids2=ids2 }; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 }
1 [HttpPost] 2 public ResultData TestParameter2([FromBody]List<int> ids, [FromBody]ProductData pd) 3 { 4 try 5 { 6 var data = new { ids = ids, pd = pd }; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 }
以上兩個示例中程式碼編譯不報錯,可以正常編譯,但是請求會報錯,報錯資訊如下:
測試程式碼:
1 function TestParameter2() { 2 var v = { ids: [1, 2, 3], ids2: [4, 5, 6] }; 3 $.ajax({ 4 type: "post", 5 url: host + "/mobileapi/test/TestParameter2", 6 dataType: "text", 7 data: { "": v }, 8 beforeSend: function (request) { 9 request.setRequestHeader("token", $("#token").val()); 10 }, 11 success: function (data) { 12 alert(data); 13 }, 14 error: function (x, y, z) { 15 alert("報錯無語"); 16 } 17 }); 18 }
1 function TestParameter2() { 2 var v = { "ids": [1, 2, 3], pd: { barcode: "ddddd" } }; 3 $.ajax({ 4 type: "post", 5 url: host + "/mobileapi/test/TestParameter2", 6 dataType: "text", 7 data: { "": v }, 8 beforeSend: function (request) { 9 request.setRequestHeader("token", $("#token").val()); 10 }, 11 success: function (data) { 12 alert(data); 13 }, 14 error: function (x, y, z) { 15 alert("報錯無語"); 16 } 17 }); 18 }
測試結果:
以上兩組測試都會報無法將多個引數繫結到請求的內容異常
3. 陣列引數的正確傳遞
調整一下API,只接收一個FromBody引數,併為List<int>
1 [HttpPost] 2 public ResultData TestParameter2([FromBody]List<int> ids) 3 { 4 try 5 { 6 var data = new { ids = ids, pd = pd }; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 }
測試一:
1 //TestParameter 2 function TestParameter2() { 3 var v = { "ids": [1, 2, 3] }; 4 $.ajax({ 5 type: "post", 6 url: host + "/mobileapi/test/TestParameter2", 7 dataType: "text", 8 data: { "": v }, 9 beforeSend: function (request) { 10 request.setRequestHeader("token", $("#token").val()); 11 }, 12 success: function (data) { 13 alert(data); 14 }, 15 error: function (x, y, z) { 16 alert("報錯無語"); 17 } 18 }); 19 }
測試一的結果:
測試二:
1 //TestParameter 2 function TestParameter2() { 3 var v = { "": [1, 2, 3] }; 4 $.ajax({ 5 type: "post", 6 url: host + "/mobileapi/test/TestParameter2", 7 dataType: "text", 8 data: { "": v }, 9 beforeSend: function (request) { 10 request.setRequestHeader("token", $("#token").val()); 11 }, 12 success: function (data) { 13 alert(data); 14 }, 15 error: function (x, y, z) { 16 alert("報錯無語"); 17 } 18 }); 19 }
測試二結果:
分析: 經過測試一和測試二結果觀察,資料傳遞格式不能陣列名,其實這個不是陣列傳遞的問題,而是WEB API frombody引數的一種約定,所以每個API只能有一個frombody格式的引數,因為這樣的引數不會依據屬性名對應解析,而是把body中傳送的所有資料解析成一個物件,所以無法定義多個 frombody 格式引數。
結論: 經過frombody修飾的引數只能有一個,並且經過frombody修飾的引數無論是簡單型別,還是複雜型別(包括陣列,list,系統class和自定義class等),傳參都不需要屬性名,屬性名必需為空字串(“”),否則無法解析,引數永遠為null
引數格式的列舉:
簡單型別json引數格式: {"":1} 、{"":1.0} 、{"":"test"} 、{"":"C"} 、{"":"2016-03-10"} 、{"":"BC069BF1-1382-4C5D-B3B1-9643F3F94A9D"}
類定義如下:
1 public class User 2 { 3 public string Name { get; set; } 4 public int Age { get; set; } 5 public DateTime? Birthday { get; set; } 6 }
類型別json引數格式:{name:"Tom", age:18, birthday:"2016-03-10"}
4. FromUrl引數和FromBody引數混合使用,這種混合使用規則參照以上使用方法,沒難度。
1 [HttpPost] 2 public ResultData TestParameter2(int i, string s, [FromUri]bool b, DateTime? t, Guid? g = null, [FromBody]List<int> ids = null) 3 { 4 try 5 { 6 var data = new { ids = ids }; 7 return new ResultData(data); 8 } 9 catch (Exception ex) 10 { 11 throw ex; 12 //return new ResultData(ResultType.SystemException, ex.Message); 13 } 14 }
此篇到此結束,歡迎大家討論!
補充:API中Action引數傳遞方式總結說明
1. 引數不帶任何屬性標籤 Action F1(string a, string b)
querystring中必需含有 a和b查詢字串 url?a=1&b=, 即使b沒有值,也必需攜帶,否則報404錯誤,找不到符合請求的Action
2. 引數帶有fromuri屬性 Action F2(string a, [FromUri]stirng b)
querystring中必需含有 a查詢字串,b可以不帶,因為被屬性[FromUri]修飾,例: url?a=1&b=或url?a=1都能匹配到F2 action, b沒有值可以不攜帶,但引數a必需攜帶
3. 帶有fromuri屬性的引數可以有多個 Action F3(string a, [FromUri]stirng b,[FromUri]stirng c,[FromUri]stirng d) ,
請求方法同2, 例如: url?a=1&b=2&d=3
4. 帶有frombody屬性的引數只適應post請求,否則永遠為null ,並且可以與fromuri混合使用,但每個action,frombody屬性引數有且僅有一個,多個會報錯
Action F4(string a, [FromUri]stirng b,[FromUri]stirng c,[FromBody]stirng d)
使用方法同 2, 3
這樣混合使用不報錯,也能正常執行,但需要混合使用的環境比較少,基本上95% Action只用fromuri或frombody就能完成,沒必要混合使用