第二節:如何正確使用WebApi和使用過程中的一些坑


一. 基本調用規則

1. 前提

  WebApi的默認路由規則為:routeTemplate: "api/{controller}/{id}", 下面為我們統一將它改為 routeTemplate: "api/{controller}/{action}/{id}",這樣我們在調用的時候,還是通過拼接方法名來識別,不用考慮上面的坑別的規則了,這里我單純的來探討WebApi的傳參和調用。

2. 基本的調用規則

  是什么請求,在方法上面標注什么特性,常見的有[HttpGet][HttpPost][HttpPut][HttpDelete]。

PS:方法名以Get、Post、Put、Delete開頭,WebApi會自動默認這個請求就是Get、Post、Put、Delete請求,而如果你以其他名稱開頭而又不標注方法的請求方式,那么這個時候服務器雖然找到了這個方法,但是由於請求方式不確定,所以直接返回給你405——方法不被允許的錯誤。

 

二. Get請求規則

1. 標准用法:

  發起請求的方式都為 api/Second/CheckLogin?userName=admin&pwd=123456 這種類型,不管幾個參數(1個或多個),接收的時候,如果是分參數接收,沒有任何問題, 可以正常接收,但如果是通過實體類來接收,必須在前面加特性[FromUri],否則接收不到。

PS:當然也可以什么參數都不寫,通過傳統的Request或者Request.QueryString來接受。

2. 實戰測試:

(1). 分別調用CheckLogin1、CheckLogin2、CheckLogin3方法, 發現前兩個方法都能正常調用,CheckLogin3這個方法報錯, 報:"Message":"出現錯誤","ExceptionMessage":"未將對象引用設置到對象的實例"。

(2). 調用CheckLogin4方法,報與上面相同的錯誤,證明dynamic類型也不能這么用。

(3). 調用CheckLogin5方法,能正常接收,表明可以通過傳統的Request或者Request.QueryString來接受。

代碼分享:

1   public class LoginModel
2     {
3         public string userName { get; set; }
4 
5         public string pwd { get; set; }
6     }
實體類
  1         #region 01-分參數接收
  2         /// <summary>
  3         /// Get api/Second/CheckLogin1?userName=admin&pwd=123456
  4         /// </summary>
  5         /// <param name="userName"></param>
  6         /// <param name="pwd"></param>
  7         /// <returns></returns>
  8         [HttpGet]
  9         public string CheckLogin1(string userName, string pwd)
 10         {
 11             if (userName == "admin" && pwd == "123456")
 12             {
 13                 return "ok";
 14             }
 15             else
 16             {
 17                 return "error";
 18             }
 19         }
 20         #endregion
 21 
 22         #region 02-用實體接收,加[FromUri]特性
 23         /// <summary>
 24         /// Get api/Second/CheckLogin2?userName=admin&pwd=123456
 25         /// </summary>
 26         /// <param name="model"></param>
 27         /// <returns></returns>
 28         [HttpGet]
 29         public string CheckLogin2([FromUri]LoginModel model)
 30         {
 31             if (model.userName == "admin" && model.pwd == "123456")
 32             {
 33                 return "ok";
 34             }
 35             else
 36             {
 37                 return "error";
 38             }
 39         }
 40         #endregion
 41 
 42         #region 03-用實體接收,不加[FromUri]特性
 43         /// <summary>
 44         /// Get api/Second/CheckLogin3?userName=admin&pwd=123456
 45         /// </summary>
 46         /// <param name="model"></param>
 47         /// <returns></returns>
 48         [HttpGet]
 49         public string CheckLogin3(LoginModel model)
 50         {
 51             if (model.userName == "admin" && model.pwd == "123456")
 52             {
 53                 return "ok";
 54             }
 55             else
 56             {
 57                 return "error";
 58             }
 59         }
 60         #endregion
 61 
 62         #region 04-用dynamic接收,加[FromUri]特性
 63         /// <summary>
 64         /// Get api/Second/CheckLogin4?userName=admin&pwd=123456
 65         /// </summary>
 66         /// <param name="model"></param>
 67         /// <returns></returns>
 68         [HttpGet]
 69         public string CheckLogin4([FromUri]dynamic model)
 70         {
 71             if (model.userName == "admin" && model.pwd == "123456")
 72             {
 73                 return "ok";
 74             }
 75             else
 76             {
 77                 return "error";
 78             }
 79         }
 80         #endregion
 81 
 82         #region 05-沒有任何參數,直接用Request相關方法接收
 83         /// <summary>
 84         /// Get api/Second/CheckLogin5?userName=admin&pwd=123456
 85         /// </summary>
 86         /// <param name="model"></param>
 87         /// <returns></returns>
 88         [HttpGet]
 89         public string CheckLogin5()
 90         {
 91             var userName = HttpContext.Current.Request["userName"];
 92             var pwd = HttpContext.Current.Request.QueryString["pwd"];
 93             if (userName == "admin" && pwd == "123456")
 94             {
 95                 return "ok";
 96             }
 97             else
 98             {
 99                 return "error";
100             }
101         }
102         #endregion
服務器端代碼
 1   //一.下面是Get請求的測試
 2             //1. 分參數接收,可以正常訪問
 3             $("#getBtn1").click(function () {
 4                 $.ajax({ url: "/api/Second/CheckLogin1", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
 5             });
 6             //2. 用實體類接收,前面加[FromUrl],可以正常訪問
 7             $("#getBtn2").click(function () {
 8                 $.ajax({ url: "/api/Second/CheckLogin2", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
 9             });
10             //3. 用實體類接收,前面什么不加,報錯
11             // "Message":"出現錯誤","ExceptionMessage":"未將對象引用設置到對象的實例"
12             $("#getBtn3").click(function () {
13                 $.ajax({ url: "/api/Second/CheckLogin3", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
14             });
15             //4. 用dynamic接收,前面什么不加,報錯
16             // "Message":"出現錯誤","ExceptionMessage":"未將對象引用設置到對象的實例"
17             $("#getBtn4").click(function () {
18                 $.ajax({ url: "/api/Second/CheckLogin4", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
19             });
20             //5. 后台直接用Request或者Request.QueryString,能正常接收
21             $("#getBtn5").click(function () {
22                 $.ajax({ url: "/api/Second/CheckLogin5", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
23             });
前端JS代碼

3. 特別說明:

  建議忘掉實戰測試中的幾種錯誤情況,記住標准用法即可。

 

三. Post請求規則

1.標准用法:

  參數一定要用“實體類”接收(即使一個參數也要封裝實體類),客戶端既可以用ContentType="application/x-www-form-urlencoded"提交表單,也可以用 ContentType ="application/json"提交, 模型類前面可以加[FromBody],但只能在一個參數前面加.

2.實戰測試

(1). 當只有一個參數的情況,且加[FromBody]特性,不封裝實體,如Register0,正常的鍵值對 { userName: "admin" }能訪問通,但后台拿不到值,只有省掉鍵名{ "": "admin" },才能正常訪問,且后台能拿到值。

(2). 多個參數的情況,后台分參數接收,如Register1,正常的鍵值對提交,無法訪問,提示找不到匹配的資源。

(3). 一個或多個參數的情況,后台用實體接收,如Register2,且加[FromBody]特性,可以正常訪問,並獲取到請求值。

  ①:默認的post請求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常請求。

  ②:將post請求指定為contentType: 'application/json',且傳遞的實體格式化成Json字符串,則可以正常請求。

(4). 一個或多個參數的情況,后台用dynamic接收,且加[FromBody]特性,需要分情況討論。

  ①:默認的post請求,即ContentType="application/x-www-form-urlencoded"的形式,服務器報500錯誤。

  ②:將post請求指定為contentType: 'application/json',且傳遞的實體格式化成Json字符串,則可以正常請求。

階段總結:后台用實體接收和用dynamic類型接收,用實體接收,無論是"application/x-www-form-urlencoded"還是"application/json"都能訪問;如果用dynamic類型接收,只有"application/json"能訪問, 且參數必須是序列化后的字符串

(5). 一個或多個參數的情況,沒有參數,如Register4,通過Request或者Request.Form相關方法可以獲取請求值。

 代碼分享:

 1  #region 01-單個參數,加[FromBody]特性
 2         [HttpPost]
 3         public string Register0([FromBody]string userName)
 4         {
 5             if (userName == "admin")
 6             {
 7                 return "ok";
 8             }
 9             else
10             {
11                 return "error";
12             }
13         }
14         #endregion
15 
16         #region 02-多個參數,分參數接收
17         [HttpPost]
18         public string Register1(string userName, string pwd)
19         {
20             if (userName == "admin" && pwd == "123456")
21             {
22                 return "ok";
23             }
24             else
25             {
26                 return "error";
27             }
28         }
29         #endregion
30 
31         #region 03-用實體接收,加[FromBody]特性
32         [HttpPost]
33         public string Register2([FromBody]LoginModel model)
34         {
35             if (model.userName == "admin" && model.pwd == "123456")
36             {
37                 return "ok";
38             }
39             else
40             {
41                 return "error";
42             }
43         }
44         #endregion
45 
46         #region 04-用dynamic接收,加[FromBody]特性
47         [HttpPost]
48         public string Register3([FromBody]dynamic model)
49         {
50             if (model.userName == "admin" && model.pwd == "123456")
51             {
52                 return "ok";
53             }
54             else
55             {
56                 return "error";
57             }
58         }
59         #endregion
60 
61         #region 05-沒有任何參數,直接用Request相關方法接收
62         [HttpPost]
63         public string Register4()
64         {
65             var userName = HttpContext.Current.Request["userName"];
66             var pwd = HttpContext.Current.Request.Form["pwd"];
67             if (userName == "admin" && pwd == "123456")
68             {
69                 return "ok";
70             }
71             else
72             {
73                 return "error";
74             }
75         } 
76         #endregion
77 
78 
79     }
服務器端代碼
 1  //二.下面是Post請求的測試(默認情況下為:ContentType="application/x-www-form-urlencoded"提交表單的形式)
 2             //PS: { userName: "admin", pwd: "123456" } 這就是一個JSON對象,也可以叫實體
 3 
 4             //1. 一個參數的情況,后台分參數接收,且必須加[FromBody]特性
 5             $("#postBtn0").click(function () {
 6                 //1.1 正常拼接,可以訪問通,但是拿不到userName的值
 7                 $.ajax({ url: "/api/Second/Register0", type: "Post", data: { userName: "admin" }, success: function (data) { alert(data); } });
 8                 //1.2 沒有鍵,只有值,可以正常訪問,能拿到userName的值
 9                 //$.ajax({ url: "/api/Second/Register0", type: "Post", data: { "": "admin" }, success: function (data) { alert(data); } });
10             });
11             //2. 多個參數的情況,后台分參數接收,正常的鍵值對提交,無法訪問,找不到匹配的資源
12             $("#postBtn1").click(function () {
13                 //訪問不通
14                 $.ajax({ url: "/api/Second/Register1", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
15             });
16             //3. 一個或多個參數的情況,后台用實體接收,且加[FromBody]特性,可以正常訪問,並獲取到請求值
17             $("#postBtn2").click(function () {
18                 //情況①,默認的post請求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常請求
19                 //$.ajax({ url: "/api/Second/Register2", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
20 
21                 //情況②,將post請求指定為contentType: 'application/json',且傳遞的參數格式化成Json字符串,則可以正常訪問
22                 $.ajax({ url: "/api/Second/Register2", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } });
23 
24             });
25             //4. 一個或多個參數的情況,后台用dynamic接收,且加[FromBody]特性,需要分情況討論
26             $("#postBtn3").click(function () {
27                 //情況①,默認的post請求,即ContentType="application/x-www-form-urlencoded"的形式,服務器報500錯誤
28                 //$.ajax({ url: "/api/Second/Register3", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
29 
30                 //情況②,將post請求指定為contentType: 'application/json',且傳遞的參數格式化成Json字符串,則可以正常訪問
31                 $.ajax({ url: "/api/Second/Register3", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } });
32             });
33             //5. 一個或多個參數的情況,沒有參數,通過Request或者Request.Form相關方法可以獲取請求值
34             $("#postBtn4").click(function () {
35                 //訪問不通
36                 $.ajax({ url: "/api/Second/Register4", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
37             });
前端JS代碼

 

四. 總結

  Put和Delete請求與Post請求的規則相同,另外還有很多極端的情況,不探討了,掌握正確的用法,直接去用最佳用法即可。

  總結:記住Get請求和Post請求的標准用法以及基本的調用規則,其他坑爹的情況,可以統統忘記了,注意的是ajax的Get請求,加上一個當前時間或者隨機數的參數,使用HttpClient 等需要禁用緩存。

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM