requestgetparameter(一个WEB请求中涉及到的编解码)
request getparameter(一个WEB请求中涉及到的编解码)Java Web 涉及到的编码
用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:
图 3. 一次 HTTP 请求的编码示例(查看大图)
如上图所示一次 HTTP 请求设计到很多地方需要编解码,它们编解码的规则是什么?下面将会重点阐述一下:
URL 的编解码
用户提交一个 URL,这个 URL 中可能存在中文,因此需要编码,如何对这个 URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个 URL:
图 4.URL 的几个组成部分
上图中以 Tomcat 作为 Servlet Engine 为例,它们分别对应到下面这些配置文件中:
Port 对应在 Tomcat 的 <Connector port="8080"/> 中配置,而 Context Path 在 <Context path="/examples"/> 中配置,Servlet Path 在 Web 应用的 web.xml 中的<servlet-mapping> <servlet-name>junshanExample</servlet-name> <url-pattern>/servlets/servlet/*</url-pattern> </servlet-mapping>
<url-pattern> 中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端,这个将在后面再介绍。
上图中 PathInfo 和 QueryString 出现了中文,当我们在浏览器中直接输入这个 URL 时,在浏览器端和服务端会如何编码和解析这个 URL 呢?为了验证浏览器是怎么编码 URL 的我们选择 FireFox 浏览器并通过 HTTPFox 插件观察我们请求的 URL 的实际的内容,以下是 URL:
HTTP://localhost:8080/examples/servlets/servlet/ 君山 ?author= 君山在中文 FireFox3.6.12 的测试结果
图 5. HTTPFox 的测试结果
君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo 是 UTF-8 编码而 QueryString 是经过 GBK 编码,至于为什么会有"%"?查阅 URL 的编码规范 RFC3986 可知浏览器编码 URL 是将非 ASCII 字符按照某种编码格式编码成 16 进制数字然后将每个 16 进制表示的字节前加上"%",所以最终的 URL 就成了上图的格式了。
默认情况下中文 IE 最终的编码结果也是一样的,不过 IE 浏览器可以修改 URL 的编码格式在选项 -> 高级 -> 国际里面的发送 UTF-8 URL 选项可以取消。
从上面测试结果可知浏览器对 PathInfo 和 QueryString 的编码是不一样的,不同浏览器对 PathInfo 也可能不一样,这就对服务器的解码造成很大的困难,下面我们以 Tomcat 为例看一下,Tomcat 接受到这个 URL 是如何解码的。
解析请求的 URL 是在
org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:1protectedvoidconvertURI(MessageBytesuri,Requestrequest) 2throwsException{ 3ByteChunkbc=uri.getByteChunk(); 4intlength=bc.getLength(); 5CharChunkcc=uri.getCharChunk(); 6cc.allocate(length,-1); 7Stringenc=connector.getURIEncoding(); 8if(enc!=null){ 9B2CConverterconv=request.getURIConverter(); 10try{ 11if(conv==null){ 12conv=newB2CConverter(enc); 13request.setURIConverter(conv); 14} 15}catch(IOExceptione){...} 16if(conv!=null){ 17try{ 18conv.convert(bc,cc,cc.getBuffer().length- 19cc.getEnd()); 20uri.setChars(cc.getBuffer(),cc.getStart(), 21cc.getLength()); 22return; 23}catch(IOExceptione){...} 24} 25} 26//Defaultencoding:fastconversion 27byte[]bbuf=bc.getBuffer(); 28char[]cbuf=cc.getBuffer(); 29intstart=bc.getStart(); 30for(inti=0;i<length;i++){ 31cbuf[i]=(char)(bbuf[i+start]&0xff); 32} 33uri.setChars(cbuf,0,length); 34}
从上面的代码中可以知道对 URL 的 URI 部分进行解码的字符集是在 connector 的 <Connector URIEncoding="UTF-8"/> 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。
QueryString 又如何解析? GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值。对它们的解码是在 request.getParameter 方法第一次被调用时进行的。request.getParameter 方法被调用时将会调用
org.apache.catalina.connector.Request 的 parseParameters 方法。这个方法将会对 GET 和 POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样。POST 表单的解码将在后面介绍,QueryString 的解码字符集是在哪定义的呢?它本身是通过 HTTP 的 Header 传到服务端的,并且也在 URL 中,是否和 URI 的解码字符集一样呢?从前面浏览器对 PathInfo 和 QueryString 的编码采取不同的编码格式不同可以猜测到解码字符集肯定也不会是一致的。的确是这样 QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 ISO-8859-1,要使用 ContentType 中定义的编码就要设置 connector 的 <Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/> 中的 useBodyEncodingForURI 设置为 true。这个配置项的名字有点让人产生混淆,它并不是对整个 URI 都采用 BodyEncoding 进行解码而仅仅是对 QueryString 使用 BodyEncoding 解码,这一点还要特别注意。
从上面的 URL 编码和解码过程来看,比较复杂,而且编码和解码并不是我们在应用程序中能完全控制的,所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符,不然很可能会碰到乱码问题,当然在我们的服务器端最好设置 <Connector/> 中的 URIEncoding 和 useBodyEncodingForURI 两个参数。HTTP Header 的编解码
当客户端发起一个 HTTP 请求除了上面的 URL 外还可能会在 Header 中传递其它参数如 Cookie、redirectPath 等,这些用户设置的值很可能也会存在编码问题,Tomcat 对它们又是怎么解码的呢?
对 Header 中的项进行解码也是在调用 request.getHeader 是进行的,如果请求的 Header 项没有解码则调用 MessageBytes 的 toString 方法,这个方法将从 byte 到 char 的转化使用的默认编码也是 ISO-8859-1,而我们也不能设置 Header 的其它解码格式,所以如果你设置 Header 中有非 ASCII 字符解码肯定会有乱码。
我们在添加 Header 时也是同样的道理,不要在 Header 中传递非 ASCII 字符,如果一定要传递的话,我们可以先将这些字符用
org.apache.catalina.util.URLEncoder 编码然后再添加到 Header 中,这样在浏览器到服务器的传递过程中就不会丢失信息了,如果我们要访问这些项时再按照相应的字符集解码就好了。POST 表单的编解码
在前面提到了 POST 表单提交的参数的解码是在第一次调用 request.getParameter 发生的,POST 表单参数传递方式与 QueryString 不同,它是通过 HTTP 的 BODY 传递到服务端的。当我们在页面上点击 submit 按钮时浏览器首先将根据 ContentType 的 Charset 编码格式对表单填的参数进行编码然后提交到服务器端,在服务器端同样也是用 ContentType 中字符集进行解码。所以通过 POST 表单提交的参数一般不会出现问题,而且这个字符集编码是我们自己设置的,可以通过
request.setCharacterEncoding(charset) 来设置。
另外针对 multipart/form-data 类型的参数,也就是上传的文件编码同样也是使用 ContentType 定义的字符集编码,值得注意的地方是上传文件是用字节流的方式传输到服务器的本地临时目录,这个过程并没有涉及到字符编码,而真正编码是在将文件内容添加到 parameters 中,如果用这个编码不能编码时将会用默认编码 ISO-8859-1 来编码。HTTP BODY 的编解码
当用户请求的资源已经成功获取后,这些内容将通过 Response 返回给客户端浏览器,这个过程先要经过编码再到浏览器进行解码。这个过程的编解码字符集可以通过
response.setCharacterEncoding 来设置,它将会覆盖request.getCharacterEncoding 的值,并且通过 Header 的 Content-Type 返回客户端,浏览器接受到返回的 socket 流时将通过 Content-Type 的 charset 来解码,如果返回的 HTTP Header 中 Content-Type 没有设置 charset,那么浏览器将根据 Html 的 <meta HTTP-equiv="Content-Type" content="text/html; charset=GBK" /> 中的 charset 来解码。如果也没有定义的话,那么浏览器将使用默认的编码来解码。其它需要编码的地方
除了 URL 和参数编码问题外,在服务端还有很多地方可能存在编码,如可能需要读取 xml、velocity 模版引擎、JSP 或者从数据库读取数据等。
xml 文件可以通过设置头来制定编码格式<?xmlversion="1.0"encoding="UTF-8"?>
Velocity 模版设置编码格式:services.VelocityService.input.encoding=UTF-8
JSP 设置编码格式:<%@pagecontentType="text/html;charset=UTF-8"%>
访问数据库都是通过客户端 JDBC 驱动来完成,用 JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置 JDBC URL 来制定如MySQL:url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"
甲乙丙丁(危险化学品仓库和甲乙丙丁库的区别)甲乙丙丁(危险化学品仓库和甲乙丙丁库的区别)打开百度APP看高清图片危险化学品仓库和甲乙丙丁库的区别,看完这篇你就明白了甲乙丙丁库和危险化学品库的概念常常容易被混淆,要搞清两者的区
年年岁岁(年年岁岁花相似)年年岁岁(年年岁岁花相似)年年岁岁花相似,岁岁年年人不同这是唐代诗人刘希夷代悲白头翁中最有名的一句诗。主要意思是花朵年年都开放都在那,但赏花看花的人每年都不同。道尽了时光易逝,物是
岁岁常欢愉年年皆胜意(愿我岁岁常欢愉,祝我年年皆胜意)岁岁常欢愉年年皆胜意(愿我岁岁常欢愉,祝我年年皆胜意)愿你此生如莲,婉约细致,祝我余生从容绽放,无证无求,轮回静守。愿你和你爱的,都是内心足够坚定,灿烂又温柔的存在,山川湖海,春秋
有北斗导航吗(都是全球导航系统)有北斗导航吗(都是全球导航系统)在大部分人看来,苏美冷战威胁了整个世界的和平,还险些用核武器摧毁整个人类文明,是一场非常荒诞的大国竞争行为。但实际上,我们现在的生活中有很多东西都是
日本女明星(十大日韩美女)日本女明星(十大日韩美女)1林珍娜林珍娜不仅日韩美女排行榜中的常客,在世界美女排行榜中也有着卓越的战绩,连续多年上榜TCCandler百大最美脸蛋排行榜,并且在2014年到2016
昨天的天堂(昨日天堂今日地狱!)昨天的天堂(昨日天堂今日地狱!)明天与意外哪个先来?这个一般人还真不好回答,但是方大炭素老师却用实际行动告诉了大家。昨天的走势是这样,走势非常风骚。只要昨天买进的人,截至昨天收盘,
外国大学排名(2021CWUR世界大学排名发布)外国大学排名(2021CWUR世界大学排名发布)近期真的是排名五花八门呀!近日,世界高校排名中心(centerforworlduniversityrankings,通称cwur)公
钰字五行属什么(金字旁最吉利的名字)每个人的名字常常都寄托有父母对其的期待与祝愿之情,因此很多家长都想为2020年出生的属鼠男宝宝起一个带有寓意的名字,但是却又不知从何入手。这里为了帮助大家起名,给大家带来了有关鼠宝
斗牛士之歌钢琴曲(话说法国人和西班牙人写的)斗牛士之歌钢琴曲(话说法国人和西班牙人写的)有两首斗牛士进行曲都非常的有名气,都是世界音乐宝库中的精髓。一首是法国人乔治比才(GeorgesBizet,1838。10。251875
克罗地亚球衣(克罗地亚世界杯球衣发布)克罗地亚球衣(克罗地亚世界杯球衣发布)直播吧3月21日讯北京时间3月21日晚,克罗地亚足协官网公布了耐克为其国家队设计的世界杯主客场球衣,队长莫德里奇和前锋曼朱基奇作为模特出镜。克
说法语的国家(说法语的非洲国家阿尔及利亚)说法语的国家(说法语的非洲国家阿尔及利亚)这几年,我经常去阿尔及利亚出差,每次从北京出发时,总能遇到很多韩国人,阿尔及利亚和北京有差不多8小时的时差,所以一路飞到阿尔及利亚10几个
为谁流下潇湘去(郴江幸自绕郴山,为谁流下潇湘去)为谁流下潇湘去(郴江幸自绕郴山,为谁流下潇湘去)诗词原文踏莎行郴州旅舍宋秦观雾失楼台,月迷津渡。桃源望断无寻处。可堪孤馆闭春寒,杜鹃声里斜阳暮。驿寄梅花,鱼传尺素。砌成此恨无重数。
善待自己(善待自己,从自律开始)善待自己(善待自己,从自律开始)一直以为自由就是想做什么就做什么,后来才发现自律者才会有自由。有时候,不是优秀才自律,而是你自律了,才会变得优秀。成年人最大的自律,是坚持做自己你认
莒县博物馆(我的莒州博物馆)莒县博物馆(我的莒州博物馆)莒县历史悠久,文化底蕴深厚,是春秋时期莒国都城所在,也是山东东南部地区的文化和经济中心。悠久的历史,孕育了灿烂的莒文化,留下了丰富的古代历史文明遗存,文
午饭英文(早午饭英语中怎么说?)午饭英文(早午饭英语中怎么说?)更多精彩,微信公众号搜索华尔街英语不知道你们是不是这样只要不上班不出去早上即使醒了也得躺尸到中午慢悠悠起来,早饭和午饭也就是早午饭一起解决那么问题来
蒙古姑娘(朱元璋是如何处置10万蒙古姑娘的?)蒙古姑娘(朱元璋是如何处置10万蒙古姑娘的?)我们都知道每一个朝代更替,都是踩着无数人的鲜血建立的。当一个王朝衰败到一定程度,必然灭亡,往往会被能人取而代之。而这一次次的更新换代,
降糖神茶(原来这些茶都是降血糖高手,你喝过几种?)降糖神茶(原来这些茶都是降血糖高手,你喝过几种?)大家发现自己血糖偏高的时候是不是着急着去医院买降血糖的药呢?俗话说的好,是药三分毒,我们能少吃药就少吃药吧!那还有什么办法能够降血
笛福的名言(丹尼尔笛福经典语录)笛福的名言(丹尼尔笛福经典语录)1。最好的人也必须面对死亡往往是好人不长命,坏人活千年。丹尼尔笛福2。只要我还能划水,我就不肯被淹死,只要我还能站立,我就不肯倒下。类别理想3。美德
苹果总市值(市值2。4万亿美元!)苹果总市值(市值2。4万亿美元!)经过几天的增长,苹果的市值已经增长至2。41万亿美元,可谓是富可敌国。而且,在8月2日公布的财富世界500强中,苹果再次成为全球最赚钱的公司,年赚
仪礼注疏(中国各朝代典籍列表仪礼)仪礼注疏(中国各朝代典籍列表仪礼)仪礼为儒家十三经之一,内容记载着先秦的各种礼仪,其中以记载士大夫的礼仪为主。秦代以前篇目不详,汉代初期高堂生传仪礼十七篇。另有古文仪礼五十六篇,已
遥知不是雪为有暗香来(王安石罢相后心灰意冷)遥知不是雪为有暗香来(王安石罢相后心灰意冷)北宋名相王安石,外号拗相公。一生坚持己见,极力在北宋王朝推行其变法主张。结果不但生前遭到朝中保守派打压,死后也被文人蓄意撰文抹黑。王安石
描写清明节的诗句(杜牧名篇清明,到底哪里有问题)描写清明节的诗句(杜牧名篇清明,到底哪里有问题)晚唐大诗人杜牧的清明一诗,家喻户晓,它曾经在1990年被香港文化机构评选为十佳唐诗第二名。不过从古至今,一直有人认为这首诗写得非常糟