关于http请求编码问题的说明(转)

转载 2015年07月07日 13:57:31

在进行web开发的时候经常会遇到乱码的问题,乱码一般出现在:

1.写在jsp文件中的中文变成乱码

2.页面的中文都变成乱码

3.后台通过request.getParameter()乱码

编码的基础知识

计算机只能以字节为单位存储和传输信息,而人需要看的是字符串,字节和字符串之间的对应关系就是字符集,例如字符“中”使用UTF-8字符集映射的字节为:E4 B8 AD 三个字节,同样反过来,这三个字节通过UTF-8字符集映射便能得到“中”这个字符,不同的字符集映射规则不一样,能表示的范围也不一样,例如“中”在GB2312中对应的字节表示为:D6 D0 两个字节,字符和字节之间的转换,描述为编码和解码:

l  字符->字节:编码,例如:“中”的UTF-8编码为 E4 B8 AD

l  字节->字符:解码,例如:字节数组D0 D6根据GB2312解码为字符“中”

还有一类编码称为URI编码和URI解码,不过URI编码和解码不是字符串和字节流之间的转换,而是由一个字符串表示另一个字符串,例如:

l  “中”的UTF-8 URI编码为 %E4%B8%AD

l  字符串%E4%B8%AD根据UTF-8进行URI解码为字符“中”

可以看出来,URI编码就是将一个字符串用%+对应字符集的编码组织的字符串来表示的,在java中String类有两个常用的方法进行编码和解码:

l  getBytes :例如“中”.getBytes(“字符集”),根据指定的字符集进行编码

l  String(bytes[],”字符集”):根据自定的字符集对字节数组进行解码

为什么会出现乱码问题

浏览器和服务器通过网络相连,浏览器请求编码成字节流在网络上传输,应用服务器接收到浏览器发送过来的字节流后通过相应的字符集再解码为字符串,如果浏览器和服务器端使用了不同的字符集或者不兼容的字符集就会导致乱码问题,例如,浏览器将“中”按照UTF-8编码为字节E4 B8 AD,在网络上传输,应用服务器接收到字节后按照GBK进行解码,那么前两个字节首先被解码为“涓”了,这只是个简单的过程说明,实际的过程比这要复杂。

因此要搞清web的编码问题,很有必要先弄清请求的过程和过程中的编码和解码。web是请求响应模式,用户操作浏览器,例如点击一个按钮提交表单,或者点击一个超链接,这时浏览器就会向应用服务器发送请求,servlet容器接受到请求后,根据web.xml的设置调用相应的应用程序,应用程序根据发送的请求进行一定的逻辑处理后返回给浏览器一段html代码,浏览器根据html解析并展现给用户,这就是一次请求应答的过程,下面对各部分作更加详细的介绍:

1.    浏览器向应用服务器发送请求

浏览器向应用服务器发送请求一般通过三种方式:1.提交表单,2.超链接,3.Ajax;

1)表单提交

表单提交又分为post和get两种方法,

采用post方法时浏览器会将表单中的字符串,采用页面的字符集编码为字节流发送到服务器,

采用get方法时,浏览器首先会将表单中的值通过页面的字符集进行URI编码后拼接到action的URL后面发送到应用服务器,例如:

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>test</title>

</head>

<body>

<form action=http://www.google.com>

<input type=text name=test value="中" />

<input type=submit />

</form>

</body>

</html>

在点击提交的时候,URL为 www.google.com.hk/?test=%E4%B8%AD,可以看出“中”字被编码为%E4%B8%AD,因为当前页面的字符集为UTF-8,所以在进行get表单提交时将按照UTF-8字符集进行URI编码。

2)超链接

在超链接中一般会传递参数,有时候也会传递中文,如这段代码:<a href="http://www.google.com/?test=中">link</a>,浏览器最终发送给服务器的都是asc字符,其中的“中”不属于asc字符集,因此也会对其进行URI编码的,但是不同的浏览器采用的字符集却不一样,如上面的这个超链接片段,在windows7中,不论页面的content="text/html; charset=utf-8"还是content="text/html; charset=GBK"IE8发送的都是www.google.com.hk/?test=%D6%D0,可以看出这是GBK的URI编码,IE8对超链接中的URI编码与页面的编码无关,与系统的默认编码有关;而在windosx xp中,IE8发送的是页面编码采用的字符集进行的URI编码,如果页面编码为GBK,IE6发送的为GBK的页面编码,如果页面编码为UTF-8则只发送UTF-8的URI编码的前两个字节;在其他的浏览器中,如Firefox和chrome,其URI编码采用的是页面的编码。

 

操作系统

浏览器

页面编码

发送的请求字符串

说明

windows7

IE8中文

UTF-8

test=%D6%D0

windows7中,采用GBK字符集进行URI编码与页面的编码无关

windows7

IE8中文

GBK

test=%D6%D0

XP

IE8中文

UTF-8

test=%E4%B8%AD

xp中与采用页面编码的字符集进行URI编码

XP

IE8中文

GBK

test=%D6%D0

windows2003

IE6中文

GBK

test=%D6%D0

GBK是正确,UTF-8是不正确

windows2003

IE6中文

UTF-8

test=%E4%B8

--

chrome中文,firefox英文

UTF-8

test=%E4%B8%AD

采用页面编码的字符集进行URI编码

--

chrome中文,firefox英文

GBK

test=%D6%D0

可以看出,直接在URL中带中文,IE的不同版本在不同的操作系统中进行URI编码的结果可能不一样,chrome和firefox使用编码和表单的get方式的编码一致,因此,直接在链接中写非asc字符是很危险的,因为字符的编码方式和客户端的环境有关。所以为了避免浏览器进行不确定的URI编码,需要在程序中将中文进行URI编码后在放到URL中,JavaScript提供了encodeURI()函数,它提供的是UTF-8的URI编码,也可以通过java.net.URLEncoder.encode(str,"字符集")进行编码

3)ajax

ajax可以指定get方式或者post方式,情况和上面说的类似

2.    应用服务器获取参数

在servlet中一般通过request.getParameter()来获得浏览器发送过来的参数,需要注意的是,服务器Servlet的最底层接受到的是InputStream,也就是字节流,request.getParameter()返回的是一个字符串,因此在getParameter()方法的内部存在解码的过程,而解码所采用的字符集根据应用服务器和操作系统的不同有可能不同,ServletRequest接口提供了一个方法:setCharacterEncoding()来设置getParameter解码的字符集,这个方法必需在getParameter之前调用,通过查看tomcate的源代码发现getParameter在第一次调用时会去初始化一个map的对象,map中存储的就是参数名和参数值,这些值就是根据设置的字符集进行解码的,一旦这些对象解码完毕,下次调用就直接从map中取值,而不需要重新去解码了,所以setCharacterEncoding必需在getParameter之前调用才有作用,也有人说这个方法只对post传递参数有效,而对get方法传递的参数无效,对tomcat5确实是这样的,但是对Websphere和apsuic,setCharacterEncoding对post和get同样有效。

应用服务器

服务器所在系统默认编码

页面编码

提交方式

URI编码

setCharacterEncoding

getParameter结果

备注

websphere6.1

GBK

UTF-8

POST

-

UTF-8

正确

服务器默认配置

POST

-

GBK

错误

GET

UTF-8

UTF-8

正确

超链接

GBK

GBK

正确

tomcat5.5

GBK

UTF-8

POST

 

UTF-8

正确

未设置URIEncoding和 useBodyEncodingForURI

POST

 

GBK

错误

GET

UTF-8

UTF-8

错误

超链接

GBK

GBK

错误

apusic5.1

GBK

UTF-8

POST

 

UTF-8

正确

服务器默认配置

POST

 

GBK

错误

GET

UTF-8

UTF-8

正确

超链接

GBK

GBK

正确

从上面的表格可以看出,Websphere6.1,apusic5.1应用服务器的get和post方法其getParameter解码所用的字符集是setCharacterEncoding所指定的字符集,tomcat5的post方法使用的是setCharacterEncoding,但是get方法却不是。在回过头看看这几个试验的过程,浏览器使用post方法时将会采用页面的字符集进行编码成字节流发送到服务器,服务器接收到字节流后根据setCharacterEncoding设定的字符集进行解码,获得字符串,

也就是说,如果使用post方法提交,只要保证“页面的编码的字符集=setCharacterEncoding设置的字符集”那么getParameter获得的值就是正确的,get和超链接的方式类似,

表单使用get提交时,会根据页面的编码进行encodeURI,超链接的方式程序可以根据指定的字符集进行URI编码,

两种方式的共同点是浏览器都会进行URI编码,在Websphere中,get和超链接的方式只要“URI编码的字符集=setCharacterEncoding的字符集”,那么getParameter的结果就是正确的,而使用get提交表单时其“URI编码的字符集=页面编码的字符集”,超链接的URI编码的字符集在上面说过,chrome和Firefox浏览器中URI编码的字符集=页面编码的字符集,但是IE却不是,没有规律。

tomcat5.5中getParameter获取get方法或超链接传过来的参数时默认会用ISO8859-1进行解码,例如浏览器发送UTF-8的编码的请求,tomcat5.5的getParameter使用ISO8859-1解码,这时的结果是错的,如果要获得正确的值,需要在tomcat5.5的getParameter的时候采用UTF-8进行解码,通过设置URIEncoding="UTF-8"或者useBodyEncodingForURI="true",就能让tomcat在的getParameter时采用UTF-8解码(useBodyEncodingForURI="true"表示解码的字符集采用与页面编码相同的字符集),如果不通过配置要并且需要获得正确的值,则需要程序进行转码,因为getParameter是通过ISO8859-1解码的,所有先通过getParameter().getBytes("ISO8859-1")编码成原来的字节数组,然后使用UTF-8字符集解码为字符串:new String(getParameter().getBytes("ISO8859-1"),"UTF-8")

3.    设置浏览器的页面编码

服务器向浏览器发送的也是经过编码成字节流在网络上传输,浏览器接收到字节流之后使用指定的字符集解码成字符串再进行展现,如果这两个环节的字符集不一致也会导致乱码的问题,

例如静态HTML文件或jsp中都是以UTF-8保存的,则需要告诉浏览器用UTF-8来进行解码,

 

  • 如果是jsp可以通过<%@ page contentType="text/html; charset=UTF-8" language="java" %>来进行设置,
  • 静态文件可以通过 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 进行设置,
  • 如果在servlet中直接进行输出,可以通过response.setCharacterEncoding("UTF-8"),setContentType("text/html;charset=UTF-8"),setHeader("Content-Type","text/html;charset=UTF-8")进行设置,

 

这些操作都相当于在response的头部增加"Content-Type:text/html;charset=UTF-8"信息,

header中的编码信息的优先级要高于html的meta标签,也就是说如果serlvet中设置了setContentType("text/html;charset=UTF-8"),jsp设置了<meta http-equiv="Content-Type" content="text/html; charset=GBK"/>则浏览器会按照UTF-8字符集进行解码,

相关文章推荐

Http响应和请求的编码问题

1.HttpServletRequest请求中的编码一致     通过Html的表单向服务器提交数据,在Servlet中通过request.getParameter(key)来获得参数值,由于Http...

HttpClient数据传输的编码方式

约定 1. HttpClient版本:HttpClient4.1 2. 服务端中间件:tomcat7 HttpGet请求的URI编码设置 1. 问题及原因 使用HttpClient的H...

HTTP协议中的编码问题

HTTP协议中的编码问题    基于HTTP协议的web网站的建设中,时常会遇到显示乱码的情况。引起乱码现象的原因归根到底就是编码与解码过程的不对称,那么我们就来了解一下HTTP协议的编码规则。 ...

HttpClient 异常出现不关闭引发的血案

最近在处理百度知道接口的问题时,遇到了下面的问题。在本机测试一直没有问题,因为测试的时间太短。而放到服务器上面就出现了下面的异常。而且是一直的出现。google 一下,原来是http连接出现了异常没有...

如何从官方网站下载jdk1.7?

由于官网是E文的,很多同学不感冒,但又希望从官网下载,因为这样才更权威一点。这里就讲一下如何从官方网站下载jdk1.7。打开百度首页,搜索:jdk1.7官方下载,找到图中所示链接,点击该链接拖动滚动条...

Android 发送HTTP请求出现编码问题

刚入门Android开发,在用HttpPost方式向服务器发送请求时,遇到编码问题,新手入门的坑,写下分享下,希望对大家有些帮助 首先 content 是我要发送的内容。 用 Httpost发...

Http请求参数编码问题

一、Http请求参数编码问题: 1、问题: 1)页面源码,样式: 2)servlet代码,结果: 3)问题分析: 4)解决问题: 方式一:手动解码: String name ...

正确处理下载文件时HTTP头的编码问题(Content-Disposition)

最近在做项目时遇到了一个 case :需要实现一个强制下载功能(即强制弹出下载对话框),并且文件名必须保持和用户之前上传时相同(可能包含非 ASCII 字符)。 前一个需求很容易实现:使用 HT...

Http协议参数传递中编码问题(Get/Post 方式)

以下内容是我转载的,觉得挺好的。。。。 一、问题:     编码问题是JAVA初学者在web开发过程中经常会遇到问题,网上也有大量相关的文章介绍,但其中很多文章并没有对URL中使用了中文等非A...
  • liuzx32
  • liuzx32
  • 2014年07月22日 19:50
  • 7198

正确处理浏览器在下载文件时HTTP头的编码问题(Content-Disposition)

最近在做项目时遇到了一个 case :需要实现一个强制在浏览器中的下载功能(即强制让浏览器弹出下载对话框),并且文件名必须保持和用户之前上传时相同(可能包含非 ASCII 字符)。 前一个需求很...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于http请求编码问题的说明(转)
举报原因:
原因补充:

(最多只允许输入30个字)