Web项目乱码的原因

3 篇文章 0 订阅

Charset编码基础 

Charset全称Character Encoding或字符集编码。Charset是将字符(characters)转换成字节(bytes)或者将字节转换成字符的算法。Java内部采用unicode来表示一个字符。将unicode字符转换成字节的过程,称为“编码”;将字节恢复成unicode字符的过程,称为“解码”。

浏览器发送给WEB应用的request参数,是以字节流的方式来表示的。Request参数必须经过解码才能被Java程序所解读。用来解码request参数的charset被称为“输入字符集编码(Input Charset)”;

WEB应用返回给浏览器的response响应内容必须编码成字节流,才能被浏览器或客户端解读。用来编码response内容的charset被称为“输出字符集编码(Output Charset)”。

一般情况下,input charset和output charset是相同的。因为浏览器发送表单数据时,总是采用当前页面的charset来编码的。例如,有一个表单页面,它的“contentType=text/html; charset=GBK”,那么用户填完全表单并提交时,浏览器会以GBK来编码用户所输入的表单数据。如果input charset和output charset不相同,服务器就不能正确解码浏览器根据output charset所发回给WEB应用的表单数据。

然而有一些例外情况下面,输入和输出的charset可能会不同:

  • 通过Java Script发送的表单,总是用UTF-8编码的。这意味着你必须用UTF-8作为input charset方能正确解码参数。这样,除非output charset也是UTF-8,否则两者就是不同的。

  • 应用间互相用HTTP访问时,可能采用不同的编码。例如,应用A以UTF-8访问应用B,而应用B是以GBK作为input/output charset的。此时会产生参数解码的错误。

  • 直接在浏览器地址栏里输入包含参数的URL,根据不同的浏览器和操作系统的设置,会有不同的结果:

    • 例如,中文Windows中,无论ie还是firefox,经试验,默认都以GBK来编码参数。IE对直接输入的参数,连URL encoding也没做。

    • 而在mac系统中,无论safari还是firefox,经试验,默认都是以UTF-8来编码参数。

Locale和charset的关系

Locale和charset是相对独立的两个参数,但是又有一定的关系。

Locale决定了要显示的文字的语言,而charset则将这种语言的文字编码成bytes或从bytes解码成文字。因此,charset必须能够涵盖locale所代表的语言文字,如果不能,则可能出现乱码。下表列举了一些locale和charset的组合:

Locale和Charset的关系

Locale 英文字符集 中文字符集 全字符集
ISO-8859-1 GB2312 Big5 GBK GB18030 UTF-8
en_US(美国英文)
zh_CN(简体中文)    
zh_TWzh_HK(台湾中文、香港中文)    

在所有charset中,有几个“全能”编码:

UTF-8

涵盖了unicode中的所有字符。然而用UTF-8来编码中文为主的页面时,每个中文会占用3个字节。建议以非中文为主的页面采用UTF-8编码。

GB18030

中文国际标准,和UTF-8一样,涵盖了unicode中的所有字符。用GB18030来编码中文为主的页面时有一定优势,因为绝大多数常用中文仅占用2个字节,比UTF-8短1/3。然而GB18030在非中文的操作系统中,有可能不能识别,其通用性不如UTF-8好。因此仅建议以中文为主的页面采用GB18030编码。

GBK

严格说,GBK不是全能编码(例如对很多西欧字符就支持不好),也不是国际标准。但它支持的字符数量接近于GB18030

设置locale和charset

在Servlet API中,以下API是和locale和charset有关的。

locale、charset相关的servlet API

HttpServletRequest
.getCharacterEncoding() 读取输入编码  
.setCharacterEncoding(charset) 设置输入编码
  • 必须在第一次调用request.getParameter() 和request.getParameterMap()前设置,否则无效。

  • 如果不设置,则默认以ISO-8859-1来解码参数。

  • 一般只影响POST请求参数的解码

.getLocale() 取得Accept-Language中浏览器首选的locale  
.getLocales() 取得所有Accept-Language中所指定的locales 
HttpServletResponse
.getCharacterEncoding() 取得输出编码  
.setCharacterEncoding(charset) 设置输出编码
  • Since Servlet 2.4

.getContentType() 取得content type
  • Since Servlet 2.4

.setContentType(contentType) 设置content type
  • Content type中可能包含charset定义,例如:text/html; charset=GBK

.getLocale() 取得输出locale  
.setLocale(locale) 设置输出locale
  • 必须在response被commit之前调用,否则无效。

  • 它同时也会设置charset,除非content type已经被设置过,并用包含了charset的定义。

设置locale和charset是一件看起来容易,做起来不容易的事:

  • 输入编码必须在第一个读取request参数的调用之前设置好,否则就无效。

  • 在Servlet 2.3之前,设置输出参数的唯一方法,是通过设置带有charset定义的content type。这一点在Servlet 2.4以后得到改进,添加了独立的设置输出编码的方法。

解析GET请求的参数

GET请求是最简单的请求方式。它的参数以URL编码的方式包含在URL中。当你在浏览器地址栏中敲入“http://localhost:8081/user/login.htm?name=%E5%90%8D%E5%AD%97&password=password”这样一个址址的时候,浏览器就会向localhost:8081服务器出如下HTTP请求:

GET /user/login.htm?name=%E5%90%8D%E5%AD%97&password=password HTTP/1.1
Host: localhost:8081

GET请求中的参数是以application/x-www-form-urlencoded方式和特定的charset编码的。假如用来编码URL参数的charset与应用的默认charset不同,那么你必须通过特殊的参数来指定charset。

GET /user/login.htm?_input_charset=UTF-8&name=%E5%90%8D%E5%AD%97&password=password HTTP/1.1

可是,上面的请求在不同的Servlet引擎中,会产生不确定的结果。这是怎么回事呢?

原来,尽管调用request.setCharacterEncoding(charset)这个方法来设置input charset编码,然而根据Servlet API的规范,这个设定只能对request content生效,而不对URL生效。换句话说,request.setCharacterEncoding(charset)方法只能用来解析POST请求的参数,而不是GET请求的参数。

那么,应该怎样处理GET请求的参数呢?根据URL规范,URL中非US-ASCII的字符必须进行基于UTF-8的URL编码。然而实际上,从浏览器到服务器,没有人完全遵守这些规范,于是便造成了一些混乱。目前应用服务器端,我们所遇到的,有下面几种不同的解码方案:

服务器对参数进行解码的逻辑

服务器 解码的逻辑
Tomcat 4
  • 根据request.setCharacterEncoding(charset)所设置的值来解码GET参数;

  • 如果未特别指定charset,则默认采用ISO-8859-1来解码参数。

Tomcat 5及更新版 以及搭载Tomcat 5以上版本的JBoss
  • 如果Tomcat配置文件conf/server.xml中设置了: <Connector useBodyEncodingForURI="true">那么根据request.setCharacterEncoding(charset)所设置的值来解码GET参数。

  • 如未设置useBodyEncodingForURI,或其值为false,则根据conf/server.xml中的配置<Connector URIEncoding="xxx">所指定的编码,来解码GET请求的参数。

  • 如未配置URIEncoding,默认采用ISO-8859-1

Jetty Server
  • Jetty总是以UTF-8来解码GET请求的参数。

综上所述,所有的应用服务器对于POST请求的参数的处理方法是没有差别的,然而对于GET请求的参数处理方法各有不同。

如果不加任何特别的设置,Tomcat最新版是以ISO-8859-1来解码GET请求的参数,而Jetty却是以UTF-8来解码的。因此,无论你以哪一种charset来编码GET请求的参数,都不可能在所有服务器上取得相同的结果 ── 除非修改服务器的配置,但这是一件既麻烦又容易出错的事情。


ref:http://openwebx.org/docs/Webx3_Guide_Book.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值