请求之密【QueryString,FormData与 RequestPayload】

最近在做AntdPro 的项目,但是使用框架的post请求后台SpringMVC接收不到参数,经过研究发现了我们所谓的请求之密。

HTTP请求中,如果是get请求,那么表单参数以name=value&name1=value1的形式附到url的后面,如果是post请求,那么表单参数是在请求体中,也是以name=value&name1=value1的形式在请求体中。我们先看一下我们的请求头
GET请求

RequestURL:http://127.0.0.1:8080/test/test.do?name=mikan&address=street
Request Method:GET
Status Code:200 OK

Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Query String Parameters
name:mikan
address:street

可以看到我们的GET请求参数是被存放在QueryString 中的(可以通过request.getParameter() 获取请求参数)
POST请求

POST请求参数的存放位置是和我们的Content-Type 有关的

1.表单提交与Jquery异步请求
RequestURL:http://127.0.0.1:8080/test/test.do
Request Method:POST
Status Code:200 OK

Request Headers
Content-Type:application/x-www-form-urlencoded

Form Data
name:mikan
address:street

可以看到我们的POST请求参数是被存放在Form Data 中的(可以通过request.getParameter() 获取请求参数)

2.JS原生异步请求XMLHttpRequest
RequestURL:http://127.0.0.1:8080/test/test.do
Request Method:POST
Status Code:200 OK

Request Headers
Content-Type:text/plain;charset=UTF-8

Request Payload
name=mikan&address=street

注意同样是POST请求,默认的Content-Type:text/plain;charset=UTF-8 而且参数存放在了Request Payload (无法通过request.getParameter() 获取请求参数)

3.文件上传
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryda3sd7BGGCFjy3P6

Request Payload
------WebKitFormBoundaryda3sd7BGGCFjy3P6
Content-Disposition: form-data; name="file"; filename="413.png"
Content-Type: image/png


------WebKitFormBoundaryda3sd7BGGCFjy3P6--

默认的Content-Type: multipart/form-data; 而且参数存放在了Request Payload

4.指定参数格式为Json的POST请求
Content-Type: application/json; charset=utf-8

Request Payload
{"option":"ZC","approveMsg":"参数","taskId":"8a948c4c5e363076015e36d0b4980004"}

默认的Content-Type: application/json; 而且参数存放在了Request Payload (无法通过request.getParameter() 获取请求参数)

基本的POST请求就上述的四种情况,我们最常用的就是表单的POST请求以及JQuery的POST异步请求,这种POST请求默认的Content-Type:application/x-www-form-urlencoded 也就是键值对的提交方式,而剩下的三种方式我们都无法通过
request.getParameter() 或者框架字段映射获取参数(这三种方式的Content-Type 都不为application/x-www-form-urlencoded), 那么原因何在呢?我们来看一下服务器接受请求的源码

protectedvoid 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;
    }

我们可以看到只有在不是文件上传且ContentType是"application/x-www-form-urlencoded" 的时候 我们会将参数存放在Map中,而我们的request.getParameter 正是从此Map中取值,所以原因就很明了了

那我们如何接受上述的2,3,4方式的参数呢?

  1. 文件上传我们需要框架的支持,这里就不多说了
  2. 我们可以设置ContentType为"application/x-www-form-urlencoded" (通用)
  3. ContentType为"application/json" 是以Json格式传输数据,我们后台可以使用@RequestBody 注解接受
  4. ContentType为"text/plain" 的,我们可以使用流进行读取
private String getStringFromStream(HttpServletRequest req) {
        ServletInputStream is;
        try {
            is = req.getInputStream();
            int nRead = 1;
            int nTotalRead = 0;
            byte[] bytes = new byte[10240];
            while (nRead > 0) {
                nRead = is.read(bytes, nTotalRead, bytes.length - nTotalRead);
                if (nRead > 0)
                    nTotalRead = nTotalRead + nRead;
            }
            String str = new String(bytes, 0, nTotalRead, "utf-8");
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }



        String  rev = getStringFromStream(request);
        Object obj = JsonUtils.jsonToObj(rev,CompleteTaskVo.class);
        if(obj instanceof  CompleteTaskVo){
            params = (CompleteTaskVo)obj;
        }

总结
HTTP POST表单请求提交时:Content-Typeapplication/x-www-form-urlencoded,而使用原生AJAX的POST请求如果不指定请求头RequestHeader,默认使用的Content-Type是text/plain;charset=UTF-8。

表单提交数据是名值对的方式,而文件上传服务器需要特殊处理,普通的post请求数据格式不固定,不一定是名值对的方式,所以服务器无法知道具体的处理方式,所以只能通过获取原始数据流的方式来进行解析。jquery在执行post请求时,会设置Content-Type为application/x-www-form-urlencoded,所以服务器能够正确解析,而使用原生ajax请求时,如果不显示的设置Content-Type,那么默认是text/plain,这时不能用request.getParameter(name)的形式获取,所以才只能通过获取原始数据流的方式来进行解析请求数据。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值