解决 HTTP 请求参数中的加号被替换为空格问题

HTTP Java Tomcat 大约 2267 字

现象

使用GET请求添加query string?后的参数)或使用Content-Typeapplication/x-www-form-urlencodedPOST请求携带参数时,加号(+号)被替换为了空格。

原因

如果URLs中出现了空格,需要用+替换,所以这里解码的时候把+转化回了空格。先有了编码的操作,所以才会有解码的操作。

Control names and values are escaped. Space characters are replaced by '+'
HTML 4.01 第 17.13.4 节,明确指出:当 Content-Type 为 application/x-www-form-urlencoded 时,对 names 和 vaules 进行转义,空格用 + 代替。

原文地址:https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1

所以因为规范的原因,上传的+号都在解码阶段转义成了空格。

源码

org.apache.tomcat.util.http.Parameters#processParameters(byte[], int, int, java.nio.charset.Charset)

判断有+号则需要解码。

private void processParameters(byte bytes[], int start, int len, Charset charset) {
    ...
    do {
        switch(bytes[pos]) {
            ...
            case '+':
                // Decoding required
                if (parsingName) {
                    decodeName = true;
                } else {
                    decodeValue = true;
                }
                pos ++;
                break;
            ...
        }
        ...
    } while (!parameterComplete && pos < end);
    ...
}

java.net.URLDecoder#decode(java.lang.String, java.lang.String)

可以看到URLDecoder解码时在匹配字符串+号时转换为了空格。

public static String decode(String s, String enc) {
    ...
    while (i < numChars) {
        c = s.charAt(i);
        switch (c) {
        case '+':
            sb.append(' ');
            i++;
            needToChange = true;
            break;

        ...
    }
    ...
}

解决

方式一

前端上传时对+号进行URLEncode处理,或者将+号统一换成%2B

HTML规范对于GET请求参数和Content-Typeapplication/x-www-form-urlencodedPOST的请求参数都要进行URLEncode处理。

方式二

使用request.getQueryString()方法代替@RequestParamrequest.getParameter()方法。

代码:

@GetMapping("/test")
public String test(@RequestParam("name") String name, HttpServletRequest request) {
    System.out.println("request param#" + name);

    String name1 = request.getParameter("name");
    System.out.println("get parameter#" + name1);

    String queryString = request.getQueryString();
    System.out.println("request query string#" + queryString);
    return name;
}

请求:

http://localhost:8080/test?name=Java+Go&age=13

输出:

request param#Java Go
get parameter#Java Go
request query string#name=Java+Go&age=13

参考

https://cloud.tencent.com/developer/article/1597945

阅读 823 · 发布于 2021-03-05

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批