第二节:如何正确使用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 });
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 });
四. 总结
Put和Delete请求与Post请求的规则相同,另外还有很多极端的情况,不探讨了,掌握正确的用法,直接去用最佳用法即可。
总结:记住Get请求和Post请求的标准用法以及基本的调用规则,其他坑爹的情况,可以统统忘记了,注意的是ajax的Get请求,加上一个当前时间或者随机数的参数,使用HttpClient 等需要禁用缓存。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。