《转载》 Content-Type引发的服务端收不到HTTP请求参数的问题


作者:萧萧慕宇
来源:CSDN
原文:https://blog.csdn.net/zuoyixiao/article/details/78717585
版权声明:本文为博主原创文章,转载请附上博文链接!

问题现象:
前端POST请求参数已经传过来了,Java后端Debug也能进到请求里,可就是取不到请求参数。

用Chrome 开发者工具可以看到请求的不同:
在这里插入图片描述在这里插入图片描述
可以看到请求参数一个在Form Data中,一个在Request Payload中,而且格式也不同。
不同的原因就在于Content-Type设置不同。

HTTP Content-Type

用于标识传输数据的类型。在请求中,Content-Type告诉服务端实际请求内容的类型;在响应中,Content-Type告诉客户端实际返回内容的类型。

HTTP定义的Content-Type类型有近200种(https://www.w3cschool.cn/http/ahkmgfmz.html),其中最常用的是以下三种:

1、application/x-www-form-urlencoded
请求参数在Form Data中,只能上传键值对,并且键值对都是间隔分开的。
参数形式: name1=value1&name2=value2

2、multipart/form-data
请求参数在Request Payload中,既可以上传文件等二进制数据,也可以上传表单键值对,只是最后会转化为一条信息。
浏览器将表单内的数据和文件放在一起发送。这种方式会定义一个不可能在数据中出现的字符串作为分隔符,然后用它将各个数据段分开。

3、application/json
请求参数在Request Payload中
参数形式: {key1:value1,key2:value2}

multipart/form-data; boundary= b o u n d / / {bound} // 其中 {bound}是一个占位符,代表我们规定的分割符,可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。

以下是一个multipart/form-data类型的请求:
在这里插入图片描述重点:对应以上三种类型Java服务端获取请求参数的方法也不同(伪代码)
1、application/x-www-form-urlencoded
1)注解@RequestParam(value=“name1”) String name1
2)注解@ModelAttribute 绑定请求参数到指定对象
3)HttpServletRequest.getParameter(“name1”)

扫描二维码关注公众号,回复: 5971781 查看本文章

2、multipart/form-data
流HttpServletRequest.getInputStream()或者HttpServletRequest.getReader()

3、application/json
1)注解@RequestBody
2)流HttpServletRequest.getInputStream()或者HttpServletRequest.getReader()

Tips: request.getParameter()、 request.getInputStream()、request.getReader()这三种方法是有冲突的,因为流只能被读一次,所以只有第一次能取到参数。

原理

那么是什么导致Content-Type类型不同取参方式也不同呢?
由于Tomcat对于Content-Type multipart/form-data(文件上传)和application/x-www-form-urlencoded(POST请求)做了“特殊处理”:
Tomcat的HttpServletRequest类的实现类为org.apache.catalina.connector.Request,而它处理请求参数的方法为protected void parseParameters(),这个方法中对Content-Type multipart/form-data(文件上传)和application/x-www-form-urlencoded(POST请求)做了处理,会解析表单数据放到request parameter map中。其他请求不会解析表单数据,所以通过request.getParameter()是获取不到的。

服务器为什么会对Content-Type application/x-www-form-urlencoded做特殊处理?

因为表单提交数据是名值对的方式,而其他的post请求(Content-Type不是application/x-www-form-urlencoded)数据格式不固定,不一定是名值对的方式,服务器无法知道具体的处理方式,所以只能通过获取原始数据流的方式来进行解析。

tomcat部分源码:

protected void parseParameters() {  
           //省略部分代码......  
           parameters.handleQueryParameters();// 这里是处理url中的参数  
           //省略部分代码......  
           if ("multipart/form-data".equals(contentType)) { // 这里是处理文件上传请求  
                parseParts();  
                success = true;  
                return;  
           }  
   
           if(!("application/x-www-form-urlencoded".equals(contentType))) {// 这里如果是非POST请求直接返回,不再进行处理  
                success = true;  
                return;  
           }  
           //下面的代码才是处理POST请求参数  
           //省略部分代码......  
           try {  
                if (readPostBody(formData, len)!= len) { // 读取请求体数据  
                    return;  
                }  
           } catch (IOException e) {  
                // Client disconnect  
                if(context.getLogger().isDebugEnabled()) {  
                    context.getLogger().debug(  
                            sm.getString("coyoteRequest.parseParameters"),e);  
                }  
                return;  
           }  
           parameters.processParameters(formData, 0, len); // 处理POST请求参数,把它放到requestparameter map中(即request.getParameterMap获取到的Map,request.getParameter(name)也是从这个Map中获取的)  
           // 省略部分代码......  
}
 
protected int readPostBody(byte body[], int len)  
   throws IOException {  
 
   int offset = 0;  
   do {  
	   int inputLen = getStream().read(body, offset, len - offset);  
	   if (inputLen <= 0) {  
			return offset;  
	   }  
	   offset += inputLen;  
   } while ((len - offset) > 0);  
   return len;  
}

实际开发中具体选用哪种Content-Type呢?
Form解析可以直接从Request对象中获取请求参数,这样对象转换与处理相对容易,但在大片JSON数据需要提交时,可能会出现大量的数据拆分与处理工作,另外针对集合类型的处理,也是其比较孱弱的地方。
而Payload的优势是一次可以提交大量JSON字符串,但无法从Request从获取参数,也会受限于JSON解析的深度(尤其是有多层对象级联的情况,最底层的对象几乎无法转换为具体类型)。

其他
jQuery在执行post请求时,会默认设置Content-Type为application/x-www-form-urlencoded,所以服务器能够正确解析,
而使用原生ajax请求时,如果不显示的设置Content-Type,那么默认是text/plain,这时服务器就不知道怎么解析数据了,所以才只能通过获取原始数据流的方式来进行解析请求数据。

jQuery中的dataType指的是预期服务器返回的数据类型,而不是发送的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。这样服务端返回json数据,前端就会获取不到返回值。


作者:萧萧慕宇
来源:CSDN
原文:https://blog.csdn.net/zuoyixiao/article/details/78717585
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/weixin_43671497/article/details/89111658