`

tomcat源码研究之参数编码格式处理

 
阅读更多

一,我们知道tomcat作为web服务器以后,我们编写的Servlet 请求中经常出现 中文乱码问题,而出现这些中文乱码,则是以下三种情况

 1),来自浏览器地址栏uri携带的中文参数

 2),来自页面链接跳转携带的中文参数

 3 ),来自表单form中提交成参数

  而这些提交方式一般以get和Post提交,那么tomcat是怎么按照什么编码格式解析这些请求参数的呢? 下面请看我的分析,

我们知道不管从get还是post来的请求参数  我们都是以reqest.getParameter(xxx) 和request.getParameterValues(xxx) 来的获取值的

所以我们先看request这两个方法:(这里的request对象是org.apache.catalina.connector.Requsest 我们知道tomcat对象我们用HttpServletRequest实现对象时RequestFacade这个对象,而这个对象却是使用设计模式中外观模式对org.apache.catalina.connector.Requsest这个对象进行了一定的封装,本质上还是看org.apache.catalina.connector.Requsest此对象)

 

    public String getParameter(String name) {

        if (!parametersParsed) {
            parseParameters();
        }

        return coyoteRequest.getParameters().getParameter(name);

    }

    public String[] getParameterValues(String name) {

        if (!parametersParsed) {
            parseParameters();
        }

        return coyoteRequest.getParameters().getParameterValues(name);

    }
// 从上可知,在没有解析之前都是先调用 parseParameters()这个方法,也就是乱码问题可能出现在这儿方法中

 下面是 parseParameters 此对象

 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
            String enc = getCharacterEncoding();

            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
            if (enc != null) {
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(enc);
                }
            } else {
                parameters.setEncoding
                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding
                        (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }
            //处理uri中参数解析
            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();
            }

            //如果消息体是文件流 则用parseParts()解析
            if ("multipart/form-data".equals(contentType)) {
                parseParts();
                success = true;
                return;
            }

            //处理body中的参数解析
            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)) {
                    if (context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.postTooLarge"));
                    }
                    checkSwallowInput();
                    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 {
                	//如果消息体是form参数 则用ReadPostBody解析
                    if (readPostBody(formData, len) != len) {
                        return;
                    }
                } catch (IOException e) {
                    // Client disconnect
                    if (context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.parseParameters"), e);
                    }
                    return;
                }
                //存放到Parametes对象中
                parameters.processParameters(formData, 0, len);
            } else if ("chunked".equalsIgnoreCase(
                    coyoteRequest.getHeader("transfer-encoding"))) {
                byte[] formData = null;
                try {
                    formData = readChunkedPostBody();
                } catch (IOException e) {
                    // Client disconnect or chunkedPostTooLarge error
                    if (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.setParseFailed(true);
            }
        }

    }

 从上述代码中可以知道 

 

1)从这个方法中 parameters.handleQueryParameters(); 解析uri参数 也就是get方式的参数

2)从这个方法中 parameters.processParameters(formData, 0, len);解析请求post的消息体参数 formdata是一个未解析的字节数组

最终 而parameters最终就是处理这两个方法调用所在(org.apache.tomcat.util.http.Parameters此对象)

(1)下面请看此对象org.apache.tomcat.util.http.Parameters的handlerQueryParameters()方法

 

 public void handleQueryParameters() {
        if( didQueryParameters ) {
            return;
        }

        didQueryParameters=true;

        if( queryMB==null || queryMB.isNull() ) {
            return;
        }

        if(log.isDebugEnabled()) {
            log.debug("Decoding query " + decodedQuery + " " +
                    queryStringEncoding);
        }

        try {
            decodedQuery.duplicate( queryMB );
        } catch (IOException e) {
            // Can't happen, as decodedQuery can't overflow
            e.printStackTrace();
        }
        //在这儿可知我们处理uri的参数编码是用的queryStringEncoding这个属性的编码
        processParameters( decodedQuery, queryStringEncoding );
    }

 (2)下面请看此对象org.apache.tomcat.util.http.Parameters的processParameters()方法

 

   

    public void processParameters( byte bytes[], int start, int len ) {
        //在这儿可知我们处理post请求消息体的bytes字节编码用的是encoding这个属性的编码
        processParameters(bytes, start, len, getCharset(encoding));
    }

 

所以综上所述我们可知处理get和post请求分别是用的org.apache.tomcat.util.http.Parameters对象中queryStringEncoding 和encoding 这两个成员变量存储的编码

 

    String encoding=null;
    String queryStringEncoding=null;

 两个属性

 

继续上述方法 

 

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
            String enc = getCharacterEncoding();

            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
           //不为空使用设置新的编码格式
            if (enc != null) {
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(enc);
                }
            //为空使用默认编码格式
            } else {
                parameters.setEncoding
                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding
                        (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }

 

 

有上述代码可知 getCharacterEncoding()和useBodyEncodingForURI 这两个会覆盖 encoding和queryStringEncoding两属性的值 从而影响解码

若使用默认编码格式 则是"ISO-8859-1”格式

 

public final class Constants {


    // -------------------------------------------------------------- Constants

    public static final String Package = "org.apache.coyote";
    
    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";

    public static final int MAX_NOTES = 32;

 

 

若不是采用默认格式编码 ,那么我们有哪些方式可以设置呢

1)可以从 getCharacterEncoding()(也就是 org.apache.coyote.Request.getCharacterEncoding() 注意最终到了org.apache.coyote.Request.getCharacterEncoding()此方法)

 

    public String getCharacterEncoding() {

        if (charEncoding != null)
            return charEncoding;

        charEncoding = ContentType.getCharsetFromContentType(getContentType());
        return charEncoding;

    }

 上述代码可知  如果charEncoding 为空 ,那么我们采取的是页面http头中的ContentType:text/html;charset=xxx 这样的编码格式, 所有我们要是设置好charEncoding就会采用我们自己的编码格式

 

 而设置charEncoding属性却是org.apache.catalina.connector.Requsest

 

    public void setCharacterEncoding(String enc)
        throws UnsupportedEncodingException {

        if (usingReader) {
            return;
        }

        // Ensure that the specified encoding is valid
        byte buffer[] = new byte[1];
        buffer[0] = (byte) 'a';

        // Confirm that the encoding name is valid
        B2CConverter.getCharset(enc);

        // Save the validated encoding
        //org.apache.coyote.Request.setCharacterEncoding(xxx)方法
        coyoteRequest.setCharacterEncoding(enc);
    }

  这时回到了 org.apache.catalina.connector.Requsest的setCharacterEncoding此方法 此方法是不是我们很熟悉啊, 它的调用就是我们上面所说的RequestFacde对象的setCharacterEncoding方法 也就是我们应用程序中HttpServletRequest的setCharacterEncoding的方法

 

2)从org.apache.tomcat.util.http.Parameters的queryStringEncoding属性设置

而此处设置却是来源于connector的U

 public void service(org.apache.coyote.Request req,
                        org.apache.coyote.Response res)
        throws Exception {

        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);

        if (request == null) {

            // Create objects
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);

            // Link objects
            request.setResponse(response);
            response.setRequest(request);

            // Set as notes
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);

            // Set query string encoding
            req.getParameters().setQueryStringEncoding
                (connector.getURIEncoding());

        }
//省略代码

 从最后一行代码可知其实queryStringEncoding的设置还是来源于connector的URIEncoding 属性的设置

3)可以从 connector的useBodyEncodingForURI 

 

二,综上所述 我们可以设置的编码格式地方有以下两点:

(1),connector的URIEncoding和useBodyEncodingForURI属性

 也就是修改tomcat下conf/server.xml中的connector节点 如:

  <Connector port="8080" protocol="HTTP/1.1"

           connectionTimeout="20000"   redirectPort="8443" useBodyEncodingForURI='true' URIEncoding='UTF-8' />

(2)   以及代码中 request.setsetCharacterEncoding 

 

 所以我们再次根据 这段代码可以总结

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
            String enc = getCharacterEncoding();

            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
           //不为空使用设置新的编码格式
            if (enc != null) {
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(enc);
                }
            //为空使用默认编码格式
            } else {
                parameters.setEncoding
                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding
                        (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }

 (1)默认情况下get请求 按照 queryStringEncoding 属性解码 即默认为org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING="ISO-8859-1" post 按照 encoding解码 也是"ios-8859-1"

    当设置了 useBodyEncodingForURI=true uri 解析就根据页面http消息头的Content-type解析

(2)如果设置了 request.setsetCharacterEncoding 编码 那么 post请求的encoding一定是根据这个设置的结果,

  get请求 如下

   1,useBodyEncodingForURI=false 还是则看URIEncoding='UTF-8' 属性的设置 如果这个属性还是为空 则使用默认的 "ISO-8859-1" 如果设置了 则用 URIEncoding这个值

        2,如果 useBodyEncodingForURI=true 则根据request.setsetCharacterEncoding 这个设置

 

 

 

分享到:
评论

相关推荐

    tomcat源码解析

    ### tomcat源码解析 #### 简介与概览 Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用的开发与部署环境中。它不仅支持Servlet API,还支持JSP规范,使得开发者能够轻松地构建动态网页。本文旨在深入...

    Tomcat源码解析.pdf

    ### Tomcat源码解析知识点概览 #### 一、Tomcat概述 - **定义**:Apache Tomcat是一款开源的Servlet容器,它实现了Servlet规范,并且提供了作为Web服务器的一些特性,但其提供的性能远不及专业的Web服务器,如...

    Tomcat的jsp编译参数

    总结来说,理解和配置Tomcat的JSP编译参数是优化Java Web应用程序性能、提升开发效率和确保错误处理的关键。通过对`web.xml`的适当调整,你可以定制JSP的编译行为,使其更好地适应项目的需求。同时,了解这些参数也...

    关于\"form表单提交数据编码方式和tomcat接受数据解码方式的思考\"一文的纠错

    在深入理解Tomcat处理编码的机制时,还需要关注Tomcat内部对Servlet请求对象`HttpServletRequest`的处理。`getParameter()`和`getParameterValues()`方法默认使用ISO-8859-1,但可以通过覆盖`ServletRequest`的`set...

    Hibernatetools编码格式的问题

    例如,Hibernate Tools的配置参数,或者源码中涉及编码转换的部分。 在提供的压缩包文件“hibernatetools”中,可能包含了一些示例代码、配置文件或日志,通过分析这些内容,可以更深入地理解问题的根源,并找到...

    HttpServletRequest源码 HttpServletResponse源码

    通过阅读源码,我们可以了解请求的分块读取、参数解析、编码转换等过程,以及响应如何被组装并发送回客户端。 `HttpServletRequest`的实现类可能会包含如以下关键组件: - `Wrapper`机制,允许在不修改核心实现的...

    struts2源码研究

    Struts2 源码分析主要涉及其在Tomcat启动过程中的初始化步骤以及请求处理流程。首先,我们来看Tomcat启动时Struts2框架如何准备和执行。 在Tomcat启动时,Struts2的Filter文件被加载,具体是`...

    tomcat_连接池数据库密码加密解密方法

    - 首先,从官方网站下载对应Tomcat版本的Commons DBCP源码包。 - 打开`org.apache.commons.dbcp.BasicDataSourceFactory`类,找到处理密码的部分代码: ``` value=properties.getProperty(PROP_PASSWORD); if...

    jsp的博客系统,tomcat+sql2000

    【标题】:“jsp的博客系统,tomcat+sql2000”指的是一个基于Java Server Pages(JSP)技术构建的博客平台,该平台运行在Apache Tomcat服务器上,并使用SQL Server 2000作为数据库管理系统。 【知识点详解】 1. ...

    大觅网源码

    总的来说,"大觅网源码"提供了学习和研究Spring框架及其相关技术的机会,对于Java Web开发者来说,深入理解并实践这个源码可以帮助他们提升技能,了解完整的Web应用开发流程。在实际操作时,需要注意遵循良好的编码...

    liferay6.2源码发布

    这时,你需要更改这些文件的编码格式为UTF-8。 接下来,为了构建和运行Liferay,你需要配置Ant。在Eclipse的Ant视图中,将`build.xml`拖入,以便使用Ant任务进行构建。在工程目录中,复制`app.server.properties`...

    关于tomcat乱码以及tomcat jvm 内存溢出问题的解决方案和理论

    标题中的“关于tomcat乱码以及tomcat jvm 内存溢出问题的解决方案和理论”涉及了两个关键的IT概念:Tomcat服务器的字符编码问题和Java虚拟机(JVM)内存管理的问题。让我们逐一深入探讨这两个主题。 首先,我们来...

    面试题专场之源码和JVM篇.pdf

    在源码篇中,首先列举了多个流行的框架,如SSM(Spring、SpringMVC、Mybatis)、SpringBoot、SpringCloud、Tomcat、Netty、Dubbo、RocketMQ等,并询问应聘者是否阅读过这些框架的源码。这是为了考察应聘者对框架内部...

    JSP-Servlet中的汉字编码问题-JSP教程

    2. **请求参数编码问题**: - **GET请求**:通常情况下,GET请求中的参数会直接出现在URL中,因此浏览器会根据自身的编码格式发送这些参数。如果服务器端默认的编码格式与浏览器的编码格式不同,则可能会导致乱码。...

    Tomcat配置中文解释&解决中文问题

    总结,Tomcat配置涉及到多个方面,理解并正确配置这些参数对于处理中文字符至关重要。同时,解决中文问题不仅需要在Tomcat配置上做工作,还需要关注整个开发链路中的编码一致性,从源代码到数据库,再到客户端展示,...

    java中文乱码解决之道(7)JSP页面编码过程Java开

    这篇"java中文乱码解决之道(7)JSP页面编码过程"的主题深入探讨了如何有效地解决这个问题,提供了宝贵的Java经验和技巧。这里我们将详细解析JSP页面编码过程中的关键点,以及如何避免和解决中文乱码。 首先,理解...

    struts2 使用action属性接收中文参数(post提交)

    - Struts2支持使用注解在Action类的方法参数上,比如`@RequestParam`,配合`@InitBinder`,可以在控制器层处理编码问题。 4. **JSP页面**: - JSP页面的`;charset=UTF-8" language="java" %&gt;`指令应设置正确的...

    java web项目配置tomcat连接池的那些搓事

    在Java Web开发中,Tomcat作为一款广泛应用的开源Servlet容器,常常被用来部署和运行Web应用程序。连接池是管理数据库连接的重要工具,它可以提高数据库操作的效率和性能。本篇文章将详细探讨如何在Java Web项目中...

    spring-boot 源码

    6. **外部配置**:支持YAML或JSON格式的配置文件,以及环境变量和命令行参数,便于在不同环境中灵活配置。 关于解压问题,由于你在描述中提到在解压过程中遇到WinRAR错误,可能的原因有: 1. **文件损坏**:下载...

Global site tag (gtag.js) - Google Analytics