理清Action参数绑定,可以降低使用core mvc的时候常遇到的参数绑定失败等问题,节约开发时间。
这个故事还要从Content-Type讲起:
1. 为啥需要content-type
http是超文本传输协议,数据都是以文本的方式传输。服务器要解析出客户端传过来的数据,数据结构那么多,服务器怎么知道你传输过来的数据结构?content-type就是用来描述传输过程中的数据结构的。服务器把数据传给客户端的时候,也需要告知客户端我传给你的数据结构是啥样子的,同样是用content-type来描述。
2. content-type有哪些
- 客户端——>服务器 客户端传给服务器的常用content-type有这么几种:
- application/x-www-form-urlencoded (格式:title=test&sub=3)表单提交,默认就是这种content-type数据结构
- multipart/form-data 既可以上传文件,也可以上传键值对,数据结构比较长,不贴了,主要用来传文件。
- text/plain 纯文本,不告诉服务器我是啥数据结构,json可以传,字符串可以传,只要是文本都可以传。
- application/json 以Json格式编码数据体,方便传输结构化数据(相比键值对),广泛用于 ajax 请求
- text/xml 以xml格式编码数据体,是早期互联网广泛使用的格式
- 服务器——>客户端
- content-type对照表 http://tool.oschina.net/commons/
- 贴一个图片请求的response
Cache-Control: no-cache
Connection: keep-alive
Content-Type: image/png;charset=utf-8
Date: Sun, 24 Feb 2019 04:22:33 GMT
Expires: Sun, 1 Jan 2000 01:00:00 GMT
Pragma: must-revalidate, no-cache, private
Server: nginx
Transfer-Encoding: chunked
再贴一个json的响应头
Access-Control-Allow-Origin: *
Cache-control: private
Connection: close
Content-Encoding: gzip
Content-Type: application/json; charset=utf-8
Date: Sun, 24 Feb 2019 04:23:11 GMT
Server: nginx/1.7.10
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.6
贴一个文件下载的
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Ali-Swift-Global-Savetime: 1550982437
Cache-Control: public, max-age=31536000
Connection: keep-alive
Content-Disposition: attachment; //设置浏览器如何处置文件,inline在浏览器中显示 和 attachment打开下载对话框
filename="RO-怪物物品图片-土波利_爱给网_aigei_com.png"; filename*=utf-8' 'RO-%E6%80%AA%E7%89%A9%E7%89%A9%E5%93%81%E5%9B%BE%E7%89%87-%E5%9C%9F%E6%B3%A2%E5%88%A9_%E7%88%B1%E7%BB%99%E7%BD%91_aigei_com.png
Content-Length: 733
Content-Transfer-Encoding: binary
Content-Type: image/png
Date: Sun, 24 Feb 2019 04:27:17 GMT
EagleId: 73e7282115509824370526003e
Last-Modified: Mon, 01 Jan 0001 00:00:00 GMT
Server: Tengine
Timing-Allow-Origin: *
Via: cache7.l2et15[90,200-0,M], cache7.l2et15[91,0], cache17.cn1088[149,200-0,M], cache13.cn1088[150,0]
3. 总结
content-type 是描述服务器和客户端通信的数据格式,是非常重要的一个字段,如果请求头中设置的是application/x-www-form-urlencoded,而传过去的是json,服务器以application/x-www-form-urlencoded的数据格式去解析请求的数据,将无法获取到数据。
asp.net core mvc Action参数绑定
1. 绑定来自路由参数的数据
1.1 我叫啥
-
get请求 地址是/home/IndexGet 后面没有任何参数
-
post请求 地址是/home/IndexPost 后面没有任何参数
1.2 我的controller叫啥
-
get请求 地址是/home/IndexGet 后面没有任何参数
-
post请求 地址是/home/IndexPost 后面没有任何参数
1.3 增加路由参数
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{api=api}/{controller=Home}/{action=Index}/{id?}");
});
1.4 Model绑定和全局成员绑定
定义如下
public class HomeController : Controller
{
[ModelBinder]
public string controller { get; set; }
[ModelBinder]
public string action { get; set; }
[ModelBinder]
public string api { get; set; }
[HttpPost]
public IActionResult IndexPost(Test test)
{
return Content("");
}
[HttpGet]
public IActionResult IndexGet(Test test)
{
return Content("");
}
}
public class Test
{
public string controller { get; set; }
public string action { get; set; }
public string api { get; set; }
}
-
get请求 地址是/test/home/IndexGet 后面没有任何参数
-
post请求 地址是/test/home/IndexPost 后面没有任何参数
2. 绑定来自url参数的数据
//请求地址
/test/home/IndexGet?param1=1¶m2=2¶m3=3
3. 绑定来自header的数据
需要注意的是FromHeader必须放在类的属性上
下面这种方式,无法获取到数据
[HttpGet]
public IActionResult IndexGet([FromHeader]Test test)
{
return Content("");
}
4. 绑定DI容器中的数据
[HttpGet]
public IActionResult IndexGet([FromServices]IControllerFactory iControllerFactory)
{
return Content("");
}
上面iControllerFactory是框架初始化的时候会自动初始化到DI容器中的一个对象。
5.绑定content-type为application/x-www-form-urlencoded的数据(默认表单提交)
//这是Action
[HttpPost]
public IActionResult IndexPost(Test test)
{
return Content("");
}
//这是Model
public class Test
{
public string param1 { get; set; }
public string param2 { get; set; }
}
这是结果
application/x-www-form-urlencoded类型的绑定非常简单基本上不用做什么操作就可以绑定成功
6.绑定content-type为multipart/form-data的数据
public class HomeController : Controller
{
[HttpPost]
public IActionResult IndexPost(Test test)
{
return Content("");
}
[HttpGet]
public IActionResult IndexGet(Test test)
{
return Content("");
}
}
public class Test
{
public string param1 { get; set; }
public IFormFileCollection param2 { get; set; }
}
请求结果
7.绑定content-type为application/json的数据
请求头
POST /home/IndexPost HTTP/1.1
Host: localhost:49780
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: a17123b3-df00-4fdc-23fc-181437681135
{param1:1}
action
public class HomeController : Controller
{
[HttpPost]
public IActionResult IndexPost(Test test)
{
return Content("");
}
[HttpGet]
public IActionResult IndexGet(Test test)
{
return Content("");
}
}
public class Test
{
public string param1 { get; set; }
}
结果
没有获取到请求的值
修改Action
[HttpPost]
public IActionResult IndexPost([FromBody]Test test)
{
return Content("");
}
结果
请求头
POST /home/IndexPost HTTP/1.1
Host: localhost:49780
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 3eac37b2-f2c4-0898-eb5d-906933b92d95
{param1:"assa"}
Action和Model
[HttpPost]
public IActionResult IndexPost([FromBody]Test test)
{
return Content("");
}
public class Test
{
public int param1 { get; set; }
}
结果并没有获取到数据
需要特别注意,模型绑定的过程中的错误,不会抛出异常。异常会被记录到ModelState中。
查看ModelState可以发现是类型转换失败了,所以请求的时候如果发现请求Model绑定失败,可以检查下是否有错误。