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.0http://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. STM32F10xxx 之 System tick Timer(SYSTICK Timer)

    背景 研究STM32F10xxx定时器的时候,无意间看到了System tick Timer,于是比较深入的了解下,在此做个记录. 正文 System tick Timer是Cotex-M内核的24位 ...

  2. POJ 1804 Brainman(归并排序)

    传送门 Description Background Raymond Babbitt drives his brother Charlie mad. Recently Raymond counted ...

  3. CASE表达式的使用

    我们在开发过程中,经常需要针对一列,基于条件逻辑来返回一个值,那么,这时候就需要使用到CASE表达式了. 例如,以下对Products表的查询就在SELECT语句中使用了CASE表达式,以生成用于描述 ...

  4. c/c++面试题(5)(c++重要的概念详解)

    1.C++面向对象的三大特征? 1)封装:将客观事物封装成抽象的类,并且设计者可以对类的成员进行访问控制权限控制. 这样一方面可以做到数据的隐藏,保护数据安全;另一方面,封装可以修改类的内部 实现而不 ...

  5. Java web项目引用java项目,类型找不到

    Java web项目引用java项目,类型找不到 错误信息: java.lang.ClassNotFoundException: org.codehaus.jackson.map.ObjectMapp ...

  6. 线程调用UpdateData函数出错

    在尝试线程更新界面时,在线程中调用UpdateData(FALSE)后出现如下错误: 原因: MFC对象不支持多线程操作,不能供多个线程进程使用.子线程调用pDlg-> UpdateData(F ...

  7. 对于Java泛型的理解

    源起:查看COLLECIOTNS类 Q1:为什么java需要泛型? 因为java对于对象类型的确认在编译期,那么强制类型转换就可以通过编译,但是运行时的错误却无法避免,那么泛型的存在可以避免强制类型转 ...

  8. 【caffe】Error parsing text-format NetParameter: ****:**:Expected string.

    错误描述: prototxt中第****行,第**列缺少一个整型数或者标识符. 解决方法: 检查对应的prototxt文件,第****行,第**列是否遗漏相关信息. 我的文件是在代码新旧版本没对应好~ ...

  9. MVVMLight 1:MVVMLight介绍以及在项目中的使用

    一.MVVM 和 MVVMLight介绍 MVVM是Model-View-ViewModel的简写.类似于目前比较流行的MVC.MVP设计模式,主要目的是为了分离视图(View)和模型(Model)的 ...

  10. java 1.7新特性

    try( ... ){ ... } catch(xxx e){ ... } java1.7特性,叫做try-with-resource,实现了AutoCloseable接口的实例可以放在try(... ...