https://www.cnblogs.com/kaiblog/p/7565231.html
我们经常需要在HttpResponse中设置一些headers,我们使用Spring MVC框架的时候我们如何给Response设置Header呢?
Sooooooooooooo easy, 看下面的代码:
1 2 3 4 5 6 7 |
|
通过验证,我们可以看到test项已经被成功添加到response的头部信息
1 2 3 4 |
|
接下来,我们希望修改Content-Type,从而统一服务器端和客户端的内容编码。我们继续修改代码,
1 2 3 4 5 6 7 |
|
接下来,我们验证一下结果:
1 2 3 |
|
和我们预想的并一样,response的content-type header没有被设置成"application/json;charset=UTF-8",很令人困惑。
那么,接下来让我们来探索下Spring MVC内部是如何处理这一过程的。首先我们先要对Spring MVC框架处理Http请求的流程有一个整体的了解。
下图清晰地向大家展示了Spring MVC处理HTTP请求的流程,(图片来自网络)
具体流程如下:
1. DispatcherServlet接收到Request请求
2. HandlerMapping选择一个合适的Handler处理Request请求
3-4. 选择合适的HandlerAdapter,调用用户编写的Controller处理业务逻辑。(HandlerAdapter主要是帮助Spring MVC支持多种类型的Controller)
5. Controller将返回结果放置到Model中并且返回view名称给Handler Adapter
6. DispatcherServlet选择合适的ViewResolver来生成View对象
7-8. View对象利用Model中的数据进行渲染并返回数据
相信大家对于上面的处理流程并不陌生,上面的流程图向我们展示了SpringMVC生成ModelAndView并返回response的大体流程。
下面我们来看看我们上面代码片段的处理流程是如何进行的?
从上面的流程图我们可以看到,content-type header是单独被处理的,具体过程可以参考下面的源码(AbstractMessageConverterMethodProcessor):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
接下来,将选择好的mediatype写入到HttpOutputMessage中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
最终的Headers设置在ServletServerHttpResponse类中完成,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
从上述的代码中,我们可以看到在RequestResponseBodyMethodProcessor这个ReturnValueHandler中,media-type被单独的逻辑进行处理,因此直接在ServletResponse中设置content-type header并不能正常生效。
需要在@RequestMapping中添加produces = {} 进行设置才可以。
https://blog.csdn.net/u014209205/article/details/81147783
介绍
http协议是建立在tcp/ip协议之上的应用层协议,主要包括三个部分,状态行,头部信息,消息主体。对应一个http请求就是:请求行,请求头,请求体。
协议规定post提交的数据,必须包含在消息主体中entity-body中,但是协议并没有规定数据使用什么编码方式。开发者可以自己决定消息主体的格式。
数据发送出去后,需要接收的服务端解析成功,一般服务端会根据content-type字段来获取参数是怎么编码的,然后对应去解码。
在最早的http post请求中,只支持application/x-www-form-urlencoded,参数都是通过浏览器的url传递。其实是不支持文件上传的,这样有很多不便。在1995年的时候,出台了rfc1867,也就是《RFC 1867 From-based file upload in HTML》,用以支持文件上传。所以content-type扩充了multipart/form-data用以支持向服务器发送二进制数据。后来随着web应用的增多,增加了诸如application/json的类型。
application/x-www-form-urlencoded
在最开始的请求方式中,请求参数都是放在url中,表单提交的时候,都是以key=&value=的方式写在url后面。这也是浏览器表单提交的默认方式。
此时可以直接调用request.getInputStream或request.getReader获取到请求内容,再解析出具体的参数。后者只是对前者的一个封装,可以让调用者更方便字符内容的处理。可以看到:
@Override
public BufferedReader getReader() throws IOException {
if (this.reader == null) {
this.reader = new BufferedReader(new InputStreamReader(getInputStream(),
getCharacterEncoding()));
}
return this.reader;
}
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
也可以通过request.getParameter获取到参数。
但是需要注意,getInputStream、getReader、getParameter在一定的场景是互斥的。
multipart/form-data
此种方式多用于文件上传,表单数据都保存在http的正文部分,各个表单项之间用boundary分开。
一次完整的抓包如下:
POST /ecard/uploadFaceImage?timestamp=1531906535406 HTTP/1.0
Host: www.example.com
X-Real-IP: 183.156.142.242
X-Forwarded-For: 183.156.142.242
Connection: close
Content-Length: 230101
sign: 9a7d3b4978979ef65a12e34ae1cf7b2d
accept: */*
user-agent: Mozilla/5.0 (Linux; U; Android 6.0.1; zh-CN; OPPO R9s Build/MMB29M)
AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108
UCBrowser/11.8.8.968 UWS/2.13.1.42 Mobile Safari/537.36 UCBS/2.13.1.42_180629181124
ChannelId(1) NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:WIFI,ws:360|0|3.0)
AliApp(AP/10.1.28.560) AlipayClient/10.1.28.560 Language/zh-Hans useStatusBar/true
isConcaveScreen/false
Cookie: ssl_upgrade=0; spanner=6tlJA6NZwnkqTDN+BMhdT7lbzLPsFJUeXt2T4qEYgj0=
Accept-Encoding: gzip
Content-Type: multipart/form-data; boundary=pgRq9HriiaBmfSo5rfyEJPtcumxb4fd6o15f_3G
--pgRq9HriiaBmfSo5rfyEJPtcumxb4fd6o15f_3G
Content-Disposition: form-data; name="personCode"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit
DM1203
--pgRq9HriiaBmfSo5rfyEJPtcumxb4fd6o15f_3G
Content-Disposition: form-data; name="DM1203"; filename="123524587.jpg"
Content-Type:
Content-Transfer-Encoding: binary
图片二进制数据(特别长)
--pgRq9HriiaBmfSo5rfyEJPtcumxb4fd6o15f_3G--
HTTP/1.1 200
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Origin, No-Cache, X-Requested-With,
If-Modified-Since, Pragma, Cache-Control, Expires, Content-Type
Access-Control-Allow-Credentials: true
XDomainRequestAllowed: 1
Content-Type: application/json;charset=UTF-8
Date: Wed, 18 Jul 2018 09:35:36 GMT
Connection: close
{"retCode":1,"msg":"success","data":null}
可以看到里面有一个boundary分界,值为:pgRq9HriiaBmfSo5rfyEJPtcumxb4fd6o15f_3G,请求时,会放在Content-Type后面传到服务器,服务器根据这个边界解析数据,划分段,每一段都是一项数据。每一项中的name属性就是唯一的id
此时用request.getParameter是取不到数据的,这个时候需要通过request.getInputStream来获取数据。这时取到的是一个InputStream,无法直接取到指定的表单项。但是有很多开源的组件可以直接利用,比如apache的fileupload组件。通过这些开源的upload组件,提供的api,就可以直接从request中取得指定的表单项。
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> list = upload.parseRequest(request);
上面的代码中,接下来就可通过遍历list获取参数了。
application/json
现在越来越多的应用使用application/json,用来告诉服务端消息主体是序列化的json字符串。由于json规范的流行,各大浏览器都开始原生支持JSON.stringfy。
而且spring对这个content-Type上传的数据有很好的支持,可以直接通过@RequestBody进行接收。也是当前完美适配当前流行的RestApi。
参考
Multipart/form-data POST文件上传详解
MVC实现,使用form-data传递数据
---------------------
作者:石硕页
来源:CSDN
原文:https://blog.csdn.net/u014209205/article/details/81147783
版权声明:本文为博主原创文章,转载请附上博文链接!