Java Web乱码分析及解决方案(三)——响应乱码

响应乱码

请求乱码是客户端向服务器发送数据时,服务器解码错误。响应乱码则是服务器处理完请求后,输出到浏览器的数据被浏览器错误解码造成的显示乱码,这类乱码是最常见也是最直接的。造成这类乱码的情况最直接的一点就是服务器对Content-Type响应报文设置错误。

页面编码:

我们的页面一般来说,可能是通过下面两种方式生成的,也就是常说的静态页面和动态页面:

(1)静态页面:我们用记事本或其他IDE工具编写的页面(比如.html),这些页面在编写的时候就需要指定一个字符编码,比如我们指定为ISO-8859-1,那么我们编写中文,肯定会乱码,因为保存的时候就出现了问题。这种情况下,我们一般都会用unicode保存文件(UTF-8)。然后在页面中设置meta标签的content-type为相应编码就不会出现问题了。

(2)动态页面:程序动态输出的,比如用模板技术或者Java的JSP、Servlet拼装的页面,这类技术一般都会保持一致的编码,比如java用unicode,所以输出的中文是没问题的。我们需要设置的是Content-Type来告诉浏览器页面的编码类型。

使用JSP模板技术时,有两个参数影响编码,一个是pageEncoding,另一个是contentType,pageEncoding顾名思义指定的是页面编码,这个编码影响的是JSP文件编译为java和class文件时,容器读取JSP文件内容时使用的编码,比如,我们的JSP文件用UTF-8保存的,但是pageEncoding使用的是ISO-8859-1,那么JSP被预编译为class文件时就已经乱码了,JSP首先被翻译为java文件,java文件统一使用UTF-8,之后java编译为class文件,统一使用unicode编码(UTF-8),然后Web容器执行class文件输出HTTP包体到客户端,这个时候使用的编码是contentType指定的编码。

虽然pageEncoding和contentType的关系有点乱,但是一般来说它们的设定是有关联的,很多Web容器在设定了一个属性后,另外一个属性也会自动设置为一致的,它们两个默认值都是ISO-8859-1。实际开发时,我们都会把这两个属性值设置为一致的。而且出现乱码时,首先确定浏览器实际使用的编码和contentType是否一致,如果一致,调整pageEncoding的值一般都可以解决乱码的。

Content-Type响应报头

页面在服务端构成之后,我们就要传输给浏览器了,这个时候服务器会首先构建响应报头,有很多响应报头是容器自己处理的,比如Content-Language、Content-Length等。在众多的报头中有一个很famous很imporant的就是Context-Type报头。

这个报头告诉客户端实体主体是什么媒体类型(MIME),Web浏览器通过Content-Type来确定用什么模块处理响应的主体,同时通过附加的charset参数确定文件的编码类型,然后浏览器调用相应的模块和编码方案解码响应主体的文件。

在服务器端通过调用response. setContentType方法可以设置这个报头的属性。还要注意,我们这里讨论的是HTTP协议,但是很多Web容器不会只支持HTTP协议,HTTP协议只是它的一种情况。

setContentType、setCharsetEncoding和setLocale

response的API开头就有如下说明:

The charset for the MIME body response canbe specified explicitly using the setCharacterEncoding(java.lang.String) andsetContentType(java.lang.String) methods, or implicitly using thesetLocale(java.util.Locale)
method. Explicit specifications take precedenceover implicit specifications. If no charset is specified, ISO-8859-1 will beused. The setCharacterEncoding, setContentType, or setLocale method must becalled before getWriter and before committing the response
for the characterencoding to be used.

See the Internet RFCs such as RFC 2045 formore information on MIME. Protocols such as SMTP and HTTP define profiles ofMIME, and those standards are still evolving.

意思是,影响Response Body的字符编码的方法有三个,setContentType、setCharsetEncoding已经setLocale。(后面附注这三个方法的API说明,一手资料很重要)

通过查看API可以知道,setContentType直接设定的就是Content-Type,可以顺带指定charset。setCharsetEncoding可以不调用,调用后,相当与设定Content-Type的字符编码部分。setLocale不重要,可以不理它,它设定的是Content-Language,如果设定了Content-Type它就失效了。而且调用SetContentType设置为text/html;charset=UTF-8就可以不调用setCharsetEncoding了(小心框架做了修改)。

出现乱码:

根据上面说的,响应出现乱码,修改两个地方,一是调用Response.setContentType设定MIME和字符编码,二修改页面的HTML标签。二者保持一致就可以了。对于静态页面要确定文件保存时的编码和HTML标签指定的编码要一致。对于JSP文件还要记得多调整一个pageEncoding。

Tomcat官方给出的建议:

Tomcat在它的官方文档中给出了避免乱的建议,原文如下:

What canyou recommend to just make everything work? (How to use UTF-8 everywhere).

Using UTF-8 asyour character encoding for everything is a safe bet. This should work forpretty much every situation.

In order to completelyswitch to using UTF-8, you need to make the following changes:

1.Set URIEncoding="UTF-8" onyour <Connector> in server.xml.References: HTTPConnectorAJPConnector.

2.Use a characterencoding filter withthe default encoding set to UTF-

3.Change all your JSPs to include charset name in their contentType.

For example, use <%@page contentType="text/html; charset=UTF-8" %> forthe usual JSP pages and <jsp:directive.page contentType="text/html; charset=UTF-8" /> forthe pages in XML syntax
(aka JSP Documents).

4.Change all your servlets to set the content type for responses and to include charset name in the content type to be UTF-8.

Use response.setContentType("text/html; charset=UTF-8") or response.setCharacterEncoding("UTF-8").

5.Change any content-generation libraries you use (Velocity, Freemarker, etc.) to use UTF-8 and to specify UTF-8 in the content type of the responses that they generate.

6.Disable any valves or filtersthat may read request parameters before your character encoding filter or jsppage has a chance to set the encoding to UTF-8. For more information see http://www.mail-archive.com/[email protected]/msg21117.html.

意思是:在整改工程中使用UTF-8作为字符集,它能在所有situation中都完美的工作,为了能彻底的使用UTF-8,你需要做如下的设定:

(1)在server.xml的connector中设置URIEncoding=“UTF-8”;

(2)使用Tomcat提供的CharsetEncodingFilter(和Spring MVC的差不多);

(3)在JSP页面的page指令中设定content-type=”text/html;charset=utf-8”; 或者jsp:directive.page contentType="text/html; charset=UTF-8"

(4)在所有Servlet中都调用setContentType为“text/html;charset=utf-8”或者setCharsetEncoding方法设定Content-Type的编码为UTF-8,

(5)设置所有模板技术(比如FreeMarker、Velocity等)使用UTF-8,或明确的指定响应头的Content-Type为UTF-8;

(6)在你的字符编码过滤器工作前,不要让任何的其他Filter读取Request的参数。

总结:

页面出现乱码的问题就是浏览器对页面的解析使用的不是指定的字符集,这个时候可以先通过右键菜单看下浏览器对当前页面使用的那种字符集,然后对比下是不是Content-Type设定出现了错误,如果没错,确定下HTML标签的设定是不是出现了问题,如果两种都没问题,就要确定是不是文件保存时使用的不是指定字符集。

附注——Response的API

response.setContetType

Sets the content type of the response beingsent to the client, if the response has not been committed yet. The givencontent type may include a character encoding specification, for example, text/html;charset=UTF-8.The
response‘s character encoding is only set from the given content type ifthis method is called before getWriter is called.

This method may be called repeatedly tochange content type and character encoding. This method has no effect if calledafter the response has been committed. It does not set the response‘s characterencoding
if it is called after getWriter has been called or after theresponse has been committed.

Containers must communicate the content typeand the character encoding used for the servlet response‘s writer to the clientif the protocol provides a way for doing so. In the case of HTTP, the Content-Type headeris
used.

如果response还没有发生,那么在发送response给客户端之前设置response的content type,设置的content type可以包含字符编码,比如:text/html;charset=UTF-8。如果在getWriter被调用之前调用了这个方法,那么response的编码将会根据给定的content
type设定。

这个方法可以被重复的调用来改变content type和字符编码。如果在response提交之后调用,这个方法是无效的。如果在getWriter被调用之后或response提交之后调用这个方法,对response字符编码的设定也不会起作用。

如果协议提供了和客户端通信的content type的方式,那么容器将使用response的wirter向容器发送关于content type和字符编码的信息。在http协议下使用的是Content-Type报头。

setCharsetEncoding

Sets the character encoding (MIME charset)of the response being sent to the client, for example, to UTF-8. If thecharacter encoding has already been set bysetContentType(java.lang.String) or setLocale(java.util.Locale),this
method overrides it. Calling setContentType(java.lang.String) withthe String of text/html and
calling this method with the String of UTF-8 isequivalent with calling setContentType with the String of text/html;charset=UTF-8.

This method can be called repeatedly tochange the character encoding. This method has no effect if it is called after getWriter hasbeen called or after the response has been committed.

Containers must communicate the characterencoding used for the servlet response‘s writer to the client if the protocolprovides a way for doing so. In the case of HTTP, the character encoding iscommunicated
as part of the Content-Type header for text media types.Note that the character encoding cannot be communicated via HTTP headers if theservlet does not specify a content type; however, it is still used to encodetext written via the servlet response‘s writer.

在response被发送给客户端之前,设置字符编码类型(MIME的字符),例如,使用“UTF-8”编码。如果字符编码已经通过setContentType或者setLocale设定了,这个方法将会覆盖他们。下面的调用是相等的,调用setContentType设置content Type为text/html,调用setCharsetEncoding设置字符编码为UTF-8,等同于调用setContentType设置content
Type为text/html;charset=utf-8

可以重复调用这个方法,但是必须在getWriter之前或者response被提交之前。

容器会把字符编码告诉客户端,如果协议提供了相应的通信方式。在HTTP协议下,字符编码作为Content –Type报头的一部分提供给客户端。值得注意的是如果servlet没有指定content type,那么字符编码将不会通过Http协议发送给客户端,但是发送给客户端的文字仍然会用给定的字符集编码。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-03 21:17:09

Java Web乱码分析及解决方案(三)——响应乱码的相关文章

Java ConcurrentModificationException 异常分析与解决方案

Java ConcurrentModificationException 异常分析与解决方案http://www.2cto.com/kf/201403/286536.html java.util.ConcurrentModificationException 解决办法 http://blog.csdn.net/lipei1220/article/details/9028669 原因:Iterator做遍历的时候,HashMap被修改(bb.remove(ele), size-1),Iterato

Java Web乱码分析及解决方案(一)——GET请求乱码

引言:     在进行Web开始时,乱码是我们最经常遇到也是最基本的问题,有经验的程序猿很容易能解决,初学者则容易被泥潭困住.而且很多时候,我们即使解决了乱码问题也是不明就里,往往云里雾里. 其实乱码问题很简单,就是客户端和服务器使用了不一样的字符集导致的.也就是我们发送文件是用的字符编码和解析文件的编码不一致.所以只要搞清楚了我们的文件是怎么被编码和解码的解决乱码就很简单了.分析乱码,我们从请求乱码和响应乱码来分析,请求乱码又需要根据GET和POST来单独分析. 请求乱码--GET     请

Java Web乱码分析及解决方案——POST请求

引言 GET请求的本质表现是将请求参数放在URL地址栏中,form表单的Method为GET的情况,参数会被浏览器默认编码,所以乱码处理方案是一样的.对于POST请求乱码,解决起来要比GET简单,我们关心的重点是在Request Body中. 请求乱码--Method方式 使用Method方式发送HTTP请求时,根据HTTP协议的规定,查询参数应该在Request的Body中,例如在Chrome下可以看到URL中不含有查询参数. 浏览器的编码 浏览器对POST的Request Body编码会采用

Java Web(二) Servlet中response、request乱码问题解决

三月不减肥,五月徒伤悲,这就是我现在的状态,哈哈~ 健身.博客坚持. --WH 一.request请求参数出现的乱码问题 get请求: get请求的参数是在url后面提交过来的,也就是在请求行中, MyServlet是一个普通的Servlet,浏览器访问它时,使用get请求方式提交了一个name=小明的参数值,在doGet中获取该参数值,并且打印到控制台,发现出现乱码 出现乱码的原因: 前提知识:需要了解码表,编码,解码这三个名词的意思.我简单说一下常规的, 码表:是一种规则,用来让我们看得懂的

Java Web乱码分析及解决方案

1.  什么是URL编码. URL编码是一种浏览器用来打包表单输入的格式,浏览器从表单中获取所有的name和其对应的value,将他们以name/value编码方式作为URL的一部分或者分离的发送到服务器上. 2.  URL编码规则. 每对name/value由&分开,每对来自表单的name/value用=分开.如果用户没有输入值的那个name依旧会出现不过就是没有值. URL编码是在字符ASCII码的十六进制数的前面加上%.例如\(她的十六进制数表示为5c)的URL编码就是%5c. 3.  简

java中文编码(字符集)分析-中文乱码分析及解决方案

 注:本文部分内容摘自网络,摘抄内容版权归原作者所有.  1.      背景知识 1.1.     Http协议 1.1.1.   URL和URI 1.1.2.   媒体类型定义 HTTP 在 Content-Type(14.17 节)和 Accept(14.1 节)头部域中使用因特网媒体类型 [17],为了提供打开和可扩展的数据类型和类型协议. media-type = type "/" subtype *( ";" parameter ) type = t

java web过滤器实际应用(解决中文乱码 html标签转义功能 敏感字符过滤功能)

转载地址:http://www.cnblogs.com/xdp-gacl/p/3952405.html 在filter中可以得到代表用户请求和响应的request.response对象,因此在编程中可以使用Decorator(装饰器)模式对request.response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求. 一.Decorator设计模式 1.1.Decorator设计模式介绍 当某个对象的方法不适应业务需求时,通常有2种方式可以对方法进行增强: 编写子类,覆盖需增强的

【转】Java ConcurrentModificationException 异常分析与解决方案--还不错

原文网址:http://www.2cto.com/kf/201403/286536.html 一.单线程 1. 异常情况举例 只要抛出出现异常,可以肯定的是代码一定有错误的地方.先来看看都有哪些情况会出现ConcurrentModificationException异常,下面以ArrayList remove 操作进行举例: 使用的数据集合: ? 1 2 3 4 5 6 7 List<string> myList = new ArrayList<string>(); myList.

Java内存泄漏分析与解决方案

Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位网友分享解决这些问题的办法. 作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端.服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现java.lang.Ou