背景知识
HTTP协议请求数据结构如下:
HttpServletRequest使用时,读取InputStream的时候经常会遇到取不到的情况。出现这种问题根本原因都是InputStream只能读取一次.Servlet中读取不到的原因都是在doPost处理之前被Servlet处理了stream。可能被Servlet处理的原因有:
- 1、请求为POST
- 2、Content-Type类型为application / x-www-form-urlencoded;并且调用了getParameter()
- 3、Content-Type为multipart / form-data,并且调用了getParameter(),并且Context的allowCasualMultipartParsing为TRUE。
以上三条都可以在下面的源码注释中看到。
应用程序/ x-WWW窗体-urlencoded
会将表单内的数据转换为键值对放入BODY中只能上传键值对,并且键值对都是间隔分开的。
当Content-Type为application / x-www-form-urlencoded时,会出现request.getInputStream取不到数据的情况,具体原因如下;
- inputStream已经读取处理过。
- 调用过request.getParameter()。(其实还是上一条原因,请求内部读取过inputStream)
当首次调用getParameter时,请求中会解析参数,parseParameters(),当Content-Type为application / x-www-form-urlencoded时,会读取流进行解析,放入paramMap中。源码如下:
/** * Parse request parameters. */ protected void parseParameters( ) { parametersParsed = true ; Parameters parameters = coyoteRequest.getParameters(); boolean success = false ; try { .......... parameters.handleQueryParameters(); String contentType = getContentType(); if ( "multipart/form-data" .equals(contentType)) { // getContext().getAllowCasualMultipartParsing() False,不解析body // allowCasualMultipartParsing:默认为false,在调用HttpServletRequest.getPart* or HttpServletRequest.getParameter*方法时候是否自动转换multipart/form-data,需要servlet3.0的支持 parseParts(); success = true ; return ; } if ( !getConnector().isParseBodyMethod(getMethod()) ) { // 如果不是POST,则返回 success = true ; return ; } if (!( "application/x-www-form-urlencoded" .equals(contentType))) { // 如果不是form-urlencoded,返回 success = true ; return ; } if (readPostBody(formData, len) != len) { parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE); return ; } parameters.processParameters(formData, 0 , len); } /** * Read post body in an array. */ protected int readPostBody( byte body[], int len) throws IOException { int offset = 0 ; do { // 使用了 inputstream int inputLen = getStream().read(body, offset, len - offset); if (inputLen <= 0 ) { return offset; } offset += inputLen; } while ((len - offset) > 0 ); return len; } |
通过上述代码可以看到,当Content-Type为multipart / form-data时,也可能parsePart,导致inputStream被处理。
多部分/格式数据
它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有内容类型来说明文件类型;内容处置,用来说明字段的一些信息;
默认情况下,Contex中allowCasualMultipartParsing为False,不会自动解析Body,因此Body中的参数不能通过request.getParameter()获取。但是url中拼接的参数可以getParameter获取到,并且不会影响到inputStream的后续使用。
上传文件的方式
经过以上情况排除,上传文件可采用的方式有:
- 1,multipart / form-data,但是大部分框架都可能会做默认封装处理,所以不一定能直接通过InputStream获取到内容。例如WF中,使用UploadRequest封装了请求。
- 2,text / plain,application / json,text / html等,可以直接读取InputStream。(对应邮递员中的原始,二进制)
总之,当读取不到inputStream时,肯定被Servlet,SpringMVC或者其他框架抢先了。