1.字符编码的原由

1.1 request和response的默认编码是?

如果未指定字符编码,则Servlet规范要求使用ISO-8859-1的编码。 HTTP消息正文(请求或响应)的字符编码在Content-Type头字段中指定。 如Content-Type:text / html; charset = ISO-8859-1明确声明正在使用默认值(ISO-8859-1)。见HTTP 1.1 Specification, Section 3.7.1最后一段。

SP规范进一步指定了JSP页面的行为。 请求字符编码处理是相同的,但响应字符编码的行为略有不同。 请参阅“JSP.4.2响应字符编码”一章。 对于标准语法中的JSP页面,默认响应字符集是通常的ISO-8859-1,但对于XML语法中的字符串,它是UTF-8。

1.2 request和response的默认编码为什么是它们?

从容器角度:

先说请求编码,目前许多浏览器不会随着Content-Type发送字符编码,即Content-Type没有"charset=utf-8"这样的字段。因此,怎么对请求进行解码,Servlet 3.1和Servlet 4.0都对此进行了说明。

Servlet 3.1

在没有charset限定符的情况下,容器对请求的解码将使用ISO-8859-1字符集进行解码。为了显示Content-Type没有指定charset,request.getCharaterEncoding()将返回null;

Servlet4.0

在没有charset限定符的情况下,如果Content-Type是application / x-www-form-urlencoded,则容器用于创建请求阅读器和解析POST数据的默认编码必须是US-ASCII,任何%nn编码值必须解码为ISO-8859-1。

如何Content-Type是任何其他类型,如果客户端请求,Web应用程序(在web.xml中没有指定)或容器供应商特定配置(对于容器中的所有Web应用程序)都没有指定,则默认编码为请求容器用于创建请求读取器和解析POST数据必须是ISO-8859-1。

从上述的两个规范看出,如客户端没有设置字符编码,servlet无法知道编码,如果请求数据使用与上述默认编码不同的编码进行编码,则可能发生破坏。

——————————————————————————————————————————————————————————

从浏览器角度:

GET的默认编码:

HTTP查询字符串的编码规范(即GET请求的参数 )RFC 3986 在2.1和2.2节有说明。字符集定义为US-ASCII。任何未映射到US-ASCII的字符都必须以某种方式(也就是说没有指定)进行编码。 URI语法规范的2.1节说明US-ASCII之外的字符必须使用%转义序列进行编码:每个字符编码为文字%,后跟两个十六进制代码,表示其字符代码。因此,(字符”中“)等同于%E4%B8%AD。虽然URI规范没有强制要求URI的默认编码,但它建议使用UTF-8,特别是对于新的URI方案,并且大多数现代用户代理已经确定UTF-8用于编码URI字符。

注意:1. ISO-8859-1和ASCII兼容字符代码0x20至0x7E,因此它们通常可互换使用。

2. 使用UTF-8编码URI的现代浏览器。某些浏览器似乎使用当前页面的编码来编码链接的URI,Chrome使用UTF-8。
           3. HTML 4.0建议使用UTF-8对查询字符串进行编码。

POST的默认编码:

  较早版本的HTTP / 1.1规范(例如RFC 2616)表明,如果没有指示字符集,ISO-8859-1是基于文本的HTTP请求和响应主体的默认字符集。尽管RFC 7231删除了此缺省值,但servlet规范仍然适用。因此,servlet规范指示如果POST请求不指示编码,则必须将其处理为ISO-8859-1,但application / x-www-form-urlencoded除外,默认情况下应将其解释为US-ASCII(因为它根据定义应该只包含ASCII范围内的字符开头)。

关于POST请求的字符编码的一些注意事项:

  RFC 2616第3.4.1节规定,如果请求有"charset=charsetName",HTTP消息的接收者必须遵守Content-Type头中发件人指定的字符编码。丢失的字符允许收件人“猜测”适当的编码。

  今天的大多数Web浏览器都没有指定请求的字符集,即使它不是ISO-8859-1。这似乎违反了HTTP规范。大多数Web浏览器似乎使用用页面的编码来发送请求正文(例如,<form>元提交的参数编码是根据页面的编码来编码的)。

ContentType为application / x-www-form-urlencoded的URI编码:

HTML 4.01规范指出应该使用US-ASCII字节序列执行application / x-www-form-urlencoded(HTML表单提交的默认内容类型)的任何非字母数字字符的百分比编码。但是HTML 5将其更改为使用UTF-8字节序列,匹配URL的现代URI编码。因此,现代浏览器在使用application / x-www-form-urlencoded提交表单时用UTF-8对其进行编码。

但是,servlet规范要求servlet容器将ContentType为application / x-www-form-urlencoded的请求正文用ISO-8859-1解码,在默认配置中,由于字符集不匹配,将导致内容损坏。请参阅下文,了解如何在Tomcat中重新配置它。

HTTP Header的编码:

ARPA Internet文本消息规范的第3.1节规定Header始终采用US-ASCII编码。除此之外的任何东西都需要编码,参阅上面有关URI中查询字符串的部分。

1.3 URIEncoding&useBodyEncodingForURI是什么?

两者都是Http connector的属性。见apache-tomcat-9.0.16-src\java\org\apache\catalina\connector\Connector.java中的源码。

URIEncoding:

指定用于解码URI所用的字符集。 如果未指定,Tomat 9.0将使用UTF-8,除非org.apache.catalina.STRICT_SERVLET_COMPLIANCE系统属性设置为true,在这种情况下将使用ISO-8859-1

useBodyEncodingForURI

这指定了contentType中指定的编码是否应该用于URI查询参数,而不是使用URIEncoding。 此设置用于与Tomcat 4.1.x兼容,其中在contentType中指定的编码或使用Request.setCharacterEncoding方法显式设置的编码也用于URL中的参数。 默认值为false。

注意:1)此设置仅应用于请求的查询字符串。 与URIEncoding不同,它不会影响请求URI的路径部分。 2)如果请求字符编码未知(由浏览器提供并且未由SetCharacterEncodingFilter或使用Request.setCharacterEncoding方法的类似过滤器设置),则默认编码始终为“ISO-8859-1”。 URIEncoding设置对此默认设置没有影响。

怎么处理字符编码问题

 2.1 怎样改变GET参数的解码?

  Tomcat将使用ISO-8859-1作为整个URL的默认字符编码,包括查询字符串(“GET参数”)。
  有两种方法可以指定如何解释GET参数:

  1. 将server.xml中<Connector>元素上的URIEncoding属性设置为特定的(例如URIEncoding =“UTF-8”)。

  2. 将server.xml中<Connector>元素的useBodyEncodingForURI属性设置为true。 这将导致Connector使用请求正文的GET参数编码。

  从Tomat 8开始,<Connector>元素上的URIEncoding属性的默认值取决于“strict servlet compliance”设置。 URIEncoding的默认值是UTF-8(strict servlet compliance=false)。 如果启用了“strict servlet compliance=true”,则默认值为ISO-8859-1。

2.2 怎样改变POST参数的解码

  POST请求应指定它们发送的参数和值的编码。由于许多客户端没有显式编码,因此当ContentType为application-x-www-form-urlencoded时,使用US-ASCII解码POST请求,当ContentType为其它类型时,使用ISO-8859-1解码POST请求。

  然而,servlet规范要求,ContentType 为application / x-www-form-urlencoded的请求,URI默认解释为ISO-8859-1。但是,HTML 5 推荐使用UTF-8对URI进行编码,并且很多的浏览器都是用UTF-8对URI进行编码的。可见,servlet规范与实际的实现存在冲突。

尽管如此,servlet规范要求servlet容器对ContentType为application / x-www-form-urlencoded的URI的解释遵循任何已配置的字符编码。因此,通过将请求字符编码设置为UTF-8,可以实现对application / x-www-form-urlencoded字节序列的适当解释(见Servlet 4.0 3.12节)。

  即在应用程序中的web.xml中设置  <request-character-encoding>UTF-8<request-character-encoding>

或设置一个charsetEncoding 的filter,指定request的解码方式。也可以在Tomcat安装配置文件conf / web.xml中定义这样的过滤器,该文件将在所有Web应用程序中设置请求字符编码,而无需任何web.xml修改。事实上,最新的Tomcat版本附带了conf / web.xml中的部分,这些部分已经配置了一个过滤器,用于将请求字符编码设置为UTF-8。只需编辑conf / web.xml并取消注释名为setCharacterEncodingFilter的过滤器的定义和映射。

tips: setCharacterEncodingFilter.java源码在apache-tomcat-9.0.16-src\java\org\apache\catalina\filters。

注意:request编码设置仅在解析参数之前完成时才有效。一旦解析发生,就没有办法回来了。参数解析由请求参数名称或值的第一个方法触发。确保过滤器位于要求请求参数的任何其他过滤器之前。定位取决于WEB-INF / web.xml文件中过滤器映射声明的顺序,但是由于Servlet 3.0规范还有其他选项来控制顺序。要检查实际的顺序,可以从页面中抛出异常并检查其堆栈跟踪以获取过滤器名称。

2.3 如何在任何地方使用UTF-8。

  使用UTF-8作为一切的字符编码是一个安全的选择。 这应该适用于几乎所有情况。

  1. 在server.xml中的<Connector>上设置URIEncoding =“UTF-8”。

  2. 在Tomcat conf / web.xml文件或web app web.xml文件中设置默认请求字符编码;通过设置<request-character-encoding>或使用字符编码过滤器。

  3. 更改所有JSP以在其contentType中包含charset名称。例如,对于通常的JSP页面使用<%@ page contentType =“text / html; charset = UTF-8”%>,使用<jsp:directive.page contentType =“text / html; charset = UTF-8”/>对于XML语法中的页面。

  4. 更改所有servlet以设置响应的内容类型,并在内容类型中包含charset name为UTF-8。使用response.setContentType(“text / html; charset = UTF-8”)或response.setCharacterEncoding(“UTF-8”)。

  5. 更改您使用的任何内容生成库(Velocity,Freemarker等)以使用UTF-8并在其生成的响应的内容类型中指定UTF-8。

  6. 在字符编码过滤器或jsp页面有机会将编码设置为UTF-8之前,禁用可能读取请求参数的任何Valves或filter。

2.4 怎样测试配置是否正常工作?

<%@ page contentType="text/html; charset=UTF-8" %>
<!-- 表单的数据编码与页面的编码相同, 对于这个JSP,是UTF-8-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Character encoding test page</title>
</head>
<body>
<p>Data posted to this form was:
<%
/* 在解码参数前,设置用什么字符集解码参数 */
request.setCharacterEncoding("UTF-8");
/* 解码数据 */
out.print(request.getParameter("mydata"));
%>
</p>
<form method="POST" action="testEncoding.jsp">
<input type="text" name="mydata">
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</form>
</body>
</html>

 

URI编码总结
========================== 在URI中有两种情况使用非US-ASCII字符:
- GET方法中的查询字符串
- Servlet的路径 URI的标准编码在 (https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_in_a_URI) ,
但是这个标准并不是所有客户端一贯遵循的,因此导致很多问题。 Tomcat提供五一些功能解决这种不太理想的情况 1. Coyote HTTP/1.1连接器具有useBodyEncodingForURI属性,如果设置为true,则使用请求主体编码来解码URI查询参数。
  -Tomat9.0.16的默认值为false; 2. Coyote HTTP/1.1 connector 有一个URIEncoding属性,默认值是UTF-8。
3. 关于请求的类(o.a.t.u.http.Parameters)有一个queryStringEncoding 属性,默认值是UTF-8的值。
必须在解码参数前设置此字段方可生效。 servlet API中需要注意的问题:
1. HttpServletRequest.setCharacterEncoding()只能应用于request的实体正文request body)而不是URI。
2. HttpServletRequest.getPathInfo() 由Web容器解码。
3. HttpServletRequest.getRequestURI() 不是由容器解码。 tips:
1. 在表单中使用POST提交参数,这样参数就会是请求正文的一部分 注: 内容非原创,参考自:
https://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q6https://tools.ietf.org/html/rfc3986#section-2.1Servelt3.1
Servlet4.0
http://tomcat.apache.org/tomcat-9.0-doc/config/http.htmlhttps://bz.apache.org/bugzilla/show_bug.cgi?id=23929

Tomcat &servlet字符集编码问题的更多相关文章

  1. servlet请求编码与响应编码问题(编码不一致可能会导致乱码)

    html中的编码 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&g ...

  2. Servlet字符编码过滤器

    在Java Web程序开发中,由于Web容器内部使用编码格式并不支持中文字符集,所以,处理浏览器请求中的中文数据就会出现乱码的现象.由于Web容器使用了ISO-8859-1的编码格式,所以在Web应用 ...

  3. 修改MySQL默认字符集编码

    好记心不如烂笔头,很多东西当时没记下来,过了就忘了,下次用到时又得浪费好多时间才能解决.今天又遇到修改MySQL默认字符集编码的问题,折腾了半天解决了,赶快记录下来,以后就不用每次折腾了. 查看MyS ...

  4. 各种编码中汉字所占字节数;中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    vim settings set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936,latin1set termencoding=utf-8se ...

  5. What is the difference Apache (Http Server) and Tomcat (Servlet Container)

    The Apache Project The Apache Project is a collaborative software development effort. Its goal is to ...

  6. 中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030 内容详见: http://www.360doc.com/content/11/1004/12/6139921_1 ...

  7. MySQL字符集编码

    MySQL字符集编码总结 之前内部博客上凯哥分享了一篇关于mysql字符集的文章,之前我对mysql字符集一块基本没有深究过,看到凯哥文章后有些地方有点疑惑,遂自己去看了mysql的官方文档,并參考了 ...

  8. 中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030 cp936是微软自己发布的用在文件系统中的编码方式.而bg2312是中国国家标准.我明白mount -t vfa ...

  9. JavaScript字符集编码与解码

    一.字符集 1)字符与字节(Character) 字符是各种文字和符号的总称,包括乱码:一个字符对应1~n个字节,一字节对应8位,每位用0或1表示. 2)字符集(Character Set) 字符集是 ...

随机推荐

  1. Linux - ansible 安装

    # 安装依赖 yum install rpm-build python2-devel sshpass PyYAML python-jinja2 python-paramiko python-six p ...

  2. zabbix监控特定脚本有无生成

    1.由于权限问题,zabbix不能直接查看其它用户目录下的文件,修改sudo文件使zabbix用户能以root身份执行test命令 visudo zabbix ALL=(root) NOPASSWD: ...

  3. ytkah常用网址导航 关于网站运营等

    关于运营的网站 人人都是产品经理 产品100 爱运营 A5网站运营 姑婆那些事儿 馒头商学院 运营者 91运营网 互联网的一些事 jb51网站运营 三联网站运营 从零开始做运营 ​ 科技/互联网 cn ...

  4. 20190423 PowerDesigner 数据库模型快速建立

    后面我在做一个视频的讲解记录吧! 那种讲解记录,只是为了演示按钮功能在什么地方,这个功能的作用是什么 这个软件相对比较简单的使用步骤,主要有三步 第一. 选择好你针对的数据库版本和类型创建数据库名称基 ...

  5. 学号20175313 《Arrays和String单元测试》第八周

    目录 Arrays和String单元测试 一.String类相关方法的单元测试 二.Arrays类相关方法的单元测试 三.测试过程中遇到的问题及其解决方法 四.码云链接 五.参考资料 Arrays和S ...

  6. Cocos Creator 生命周期回调(官方文档摘录)

    Cocos Creator 为组件脚本提供了生命周期的回调函数.用户通过定义特定的函数回调在特定的时期编写相关 脚本.目前提供给用户的声明周期回调函数有: onLoad start update la ...

  7. Spring Boot入门 and Spring Boot与ActiveMQ整合

    1.Spring Boot入门 1.1什么是Spring Boot Spring 诞生时是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品.无 ...

  8. [MSF]server/capture/http_javascript_keylogger键盘记录

    server/capture/http_javascript_keylogger DEMO使用的官方的效果图: 使用方法: show options 看看 msf auxiliary(server/c ...

  9. java中进程与线程的区别

    进程是一个正在运行的应用程序.一个进程包含一个或多个线程.它可以是一段完整的代码或部分程序的动态执行.系统资源分配与调度的基本单位.而线程是CPU调度与运行的基本单位,它是一组指令的集合或是程序的特殊 ...

  10. openFileDialog的Filter属性设置

    OpenFileDialog对话框的Filter属性说明: 首先说明一个示例,分析一下Filter属性的构成:“ Excel文件|*.xls ”,前面的“Excel文件”成为标签,是一个可读的字符串, ...