一,我们知道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作为一款开源的Servlet容器,被广泛应用于Java Web应用的开发与部署环境中。它不仅支持Servlet API,还支持JSP规范,使得开发者能够轻松地构建动态网页。本文旨在深入...
### Tomcat源码解析知识点概览 #### 一、Tomcat概述 - **定义**:Apache Tomcat是一款开源的Servlet容器,它实现了Servlet规范,并且提供了作为Web服务器的一些特性,但其提供的性能远不及专业的Web服务器,如...
总结来说,理解和配置Tomcat的JSP编译参数是优化Java Web应用程序性能、提升开发效率和确保错误处理的关键。通过对`web.xml`的适当调整,你可以定制JSP的编译行为,使其更好地适应项目的需求。同时,了解这些参数也...
在深入理解Tomcat处理编码的机制时,还需要关注Tomcat内部对Servlet请求对象`HttpServletRequest`的处理。`getParameter()`和`getParameterValues()`方法默认使用ISO-8859-1,但可以通过覆盖`ServletRequest`的`set...
例如,Hibernate Tools的配置参数,或者源码中涉及编码转换的部分。 在提供的压缩包文件“hibernatetools”中,可能包含了一些示例代码、配置文件或日志,通过分析这些内容,可以更深入地理解问题的根源,并找到...
通过阅读源码,我们可以了解请求的分块读取、参数解析、编码转换等过程,以及响应如何被组装并发送回客户端。 `HttpServletRequest`的实现类可能会包含如以下关键组件: - `Wrapper`机制,允许在不修改核心实现的...
Struts2 源码分析主要涉及其在Tomcat启动过程中的初始化步骤以及请求处理流程。首先,我们来看Tomcat启动时Struts2框架如何准备和执行。 在Tomcat启动时,Struts2的Filter文件被加载,具体是`...
- 首先,从官方网站下载对应Tomcat版本的Commons DBCP源码包。 - 打开`org.apache.commons.dbcp.BasicDataSourceFactory`类,找到处理密码的部分代码: ``` value=properties.getProperty(PROP_PASSWORD); if...
【标题】:“jsp的博客系统,tomcat+sql2000”指的是一个基于Java Server Pages(JSP)技术构建的博客平台,该平台运行在Apache Tomcat服务器上,并使用SQL Server 2000作为数据库管理系统。 【知识点详解】 1. ...
总的来说,"大觅网源码"提供了学习和研究Spring框架及其相关技术的机会,对于Java Web开发者来说,深入理解并实践这个源码可以帮助他们提升技能,了解完整的Web应用开发流程。在实际操作时,需要注意遵循良好的编码...
这时,你需要更改这些文件的编码格式为UTF-8。 接下来,为了构建和运行Liferay,你需要配置Ant。在Eclipse的Ant视图中,将`build.xml`拖入,以便使用Ant任务进行构建。在工程目录中,复制`app.server.properties`...
标题中的“关于tomcat乱码以及tomcat jvm 内存溢出问题的解决方案和理论”涉及了两个关键的IT概念:Tomcat服务器的字符编码问题和Java虚拟机(JVM)内存管理的问题。让我们逐一深入探讨这两个主题。 首先,我们来...
在源码篇中,首先列举了多个流行的框架,如SSM(Spring、SpringMVC、Mybatis)、SpringBoot、SpringCloud、Tomcat、Netty、Dubbo、RocketMQ等,并询问应聘者是否阅读过这些框架的源码。这是为了考察应聘者对框架内部...
2. **请求参数编码问题**: - **GET请求**:通常情况下,GET请求中的参数会直接出现在URL中,因此浏览器会根据自身的编码格式发送这些参数。如果服务器端默认的编码格式与浏览器的编码格式不同,则可能会导致乱码。...
总结,Tomcat配置涉及到多个方面,理解并正确配置这些参数对于处理中文字符至关重要。同时,解决中文问题不仅需要在Tomcat配置上做工作,还需要关注整个开发链路中的编码一致性,从源代码到数据库,再到客户端展示,...
这篇"java中文乱码解决之道(7)JSP页面编码过程"的主题深入探讨了如何有效地解决这个问题,提供了宝贵的Java经验和技巧。这里我们将详细解析JSP页面编码过程中的关键点,以及如何避免和解决中文乱码。 首先,理解...
- Struts2支持使用注解在Action类的方法参数上,比如`@RequestParam`,配合`@InitBinder`,可以在控制器层处理编码问题。 4. **JSP页面**: - JSP页面的`;charset=UTF-8" language="java" %>`指令应设置正确的...
在Java Web开发中,Tomcat作为一款广泛应用的开源Servlet容器,常常被用来部署和运行Web应用程序。连接池是管理数据库连接的重要工具,它可以提高数据库操作的效率和性能。本篇文章将详细探讨如何在Java Web项目中...
6. **外部配置**:支持YAML或JSON格式的配置文件,以及环境变量和命令行参数,便于在不同环境中灵活配置。 关于解压问题,由于你在描述中提到在解压过程中遇到WinRAR错误,可能的原因有: 1. **文件损坏**:下载...