美文网首页Java 杂谈程序猿阵线联盟-汇总各类技术干货技术干货
http请求参数中加号被替换为空格及请求参数被URLDeCode

http请求参数中加号被替换为空格及请求参数被URLDeCode

作者: e97f2df5b7a2 | 来源:发表于2018-07-04 18:39 被阅读11次

    今天遇到两个有关tomcat的比较有意思的问题

    1.用postman模拟https请求的时候,如果请求参数是name=jay+love。最后服务端用request.getParameter("name")收到的是【jay love】
    2.遇到上诉问题之后随即想到对jay+love进行URLEnCode处理,即name=jay%2blove。最后服务端用request.getParameter("name")收到的是【jay+love】,这里被自动URLDeCode解码了。

    关于第一个问题:

    加号变空格

    在网上找到一个说法:
    html中因为一些非标准的做法,将+等同于空格进行处理(当Html的表单被提交时, 每个表单域都会被Url编码之后才在被发送。由于历史的原因,表单使用的Url编码实现并不符合最新的标准。例如对于空格使用 的编码并不是%20,而是+号,如果表单使用的是Post方法提交的,我们可以在HTTP头中看到有一个Content-Type的header ,值为application/x-www-form-urlencoded,大部分应用程序均能处理这种非标准实现的Url编码)。

    这样就解释了为什么【jay+love】变成了【jay love】。

    其次我还想到了,为什么我们在html进行form表单提交的时候并没有注意到这样的问题。因为当Html的表单被提交时, 每个表单域都会被Url编码之后才在被发送。所以这个问题就被隐藏了。

    关于第二个问题:

    自动DeCode

    我首先是想到了是调用getParameter这个方法的时候,方法内部对参数进行了URLDeCode。点进去发现方法属于这个接口javax.servlet.ServletRequest,位于javax.servlet-api.jar中。

    那么源码就位于WEB容器中,也就是tomcat的源码。下载tomcat源码后,我下载的是apache-tomcat-8.5.32-src。

    愉快的找到这个接口。发现有很多实现类。

    getParameter的实现类

    那我到底用的是哪个呢,我也不知道,那我就在调用的地方打印一下呗。System.out.println(request.getClass().getName());

    输出:org.apache.catalina.connector.RequestFacade。

    那我们看一下RequestFacade里面怎么写的。【Facade模式对Request对象进行包装】

    RequestFacade.getParameter()

    可以看到他又调用了org.apache.catalina.connector.Request.getParameter,那么这个方法干什么了呢,如图:

    org.apache.catalina.connector.Request.getParameter

    不难看出,这个方法解析参数parseParameters。并且看注释说明:

    获取指定的参数,比如我们这里的name,如果有就返回值,没有就返回null。如果获取的参数不止一个,比如请求参数中有两个name,那么只返回第一个.

     protected void parseParameters() {
    
            parametersParsed = true;
    
            Parameters parameters = coyoteRequest.getParameters();
            boolean success = false;
            try {
                // Set this every time in case limit has been changed via JMX
                parameters.setLimit(getConnector().getMaxParameterCount());
    
                // getCharacterEncoding() may have been overridden to search for
                // hidden form field containing request encoding
                Charset charset = getCharset();
    
                boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
                parameters.setCharset(charset);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringCharset(charset);
                }
                // Note: If !useBodyEncodingForURI, the query string encoding is
                //       that set towards the start of CoyoyeAdapter.service()
    
                parameters.handleQueryParameters();
    
                if (usingInputStream || usingReader) {
                    success = true;
                    return;
                }
    
                if( !getConnector().isParseBodyMethod(getMethod()) ) {
                    success = true;
                    return;
                }
    
                String contentType = getContentType();
                if (contentType == null) {
                    contentType = "";
                }
                int semicolon = contentType.indexOf(';');
                if (semicolon >= 0) {
                    contentType = contentType.substring(0, semicolon).trim();
                } else {
                    contentType = contentType.trim();
                }
    
                if ("multipart/form-data".equals(contentType)) {
                    parseParts(false);
                    success = true;
                    return;
                }
    
                if (!("application/x-www-form-urlencoded".equals(contentType))) {
                    success = true;
                    return;
                }
    
                int len = getContentLength();
    
                if (len > 0) {
                    int maxPostSize = connector.getMaxPostSize();
                    if ((maxPostSize >= 0) && (len > maxPostSize)) {
                        Context context = getContext();
                        if (context != null && context.getLogger().isDebugEnabled()) {
                            context.getLogger().debug(
                                    sm.getString("coyoteRequest.postTooLarge"));
                        }
                        checkSwallowInput();
                        parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                        return;
                    }
                    byte[] formData = null;
                    if (len < CACHED_POST_LEN) {
                        if (postData == null) {
                            postData = new byte[CACHED_POST_LEN];
                        }
                        formData = postData;
                    } else {
                        formData = new byte[len];
                    }
                    try {
                        if (readPostBody(formData, len) != len) {
                            parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
                            return;
                        }
                    } catch (IOException e) {
                        // Client disconnect
                        Context context = getContext();
                        if (context != null && context.getLogger().isDebugEnabled()) {
                            context.getLogger().debug(
                                    sm.getString("coyoteRequest.parseParameters"),
                                    e);
                        }
                        parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
                        return;
                    }
                    parameters.processParameters(formData, 0, len);
                } else if ("chunked".equalsIgnoreCase(
                        coyoteRequest.getHeader("transfer-encoding"))) {
                    byte[] formData = null;
                    try {
                        formData = readChunkedPostBody();
                    } catch (IllegalStateException ise) {
                        // chunkedPostTooLarge error
                        parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                        Context context = getContext();
                        if (context != null && context.getLogger().isDebugEnabled()) {
                            context.getLogger().debug(
                                    sm.getString("coyoteRequest.parseParameters"),
                                    ise);
                        }
                        return;
                    } catch (IOException e) {
                        // Client disconnect
                        parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
                        Context context = getContext();
                        if (context != null && context.getLogger().isDebugEnabled()) {
                            context.getLogger().debug(
                                    sm.getString("coyoteRequest.parseParameters"),
                                    e);
                        }
                        return;
                    }
                    if (formData != null) {
                        parameters.processParameters(formData, 0, formData.length);
                    }
                }
                success = true;
            } finally {
                if (!success) {
                    parameters.setParseFailedReason(FailReason.UNKNOWN);
                }
            }
    
        }
    

    可以看到第三行:

    Parametersparameters =coyoteRequest.getParameters();
    

    直接获取Parameters。这不就是我们想要的东西嘛。点进去看看。

    image

    就是一个简单的get方法。看一下这个成员变量的定义。

    image

    接下来看看那些地方调用了他。

    image

    矮油,我去这一行调用这名字很可疑呀,去看看。

    image

    恍然大悟,原来是构造方法的时候就调用了URLDecoder了。至此,我大概知道了这个流程。

    今天有幸遇到这个问题,特此记录。

    相关文章

      网友评论

        本文标题:http请求参数中加号被替换为空格及请求参数被URLDeCode

        本文链接:https://www.haomeiwen.com/subject/reoeuftx.html