`
hejiajunsh
  • 浏览: 410739 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

web请求编码的问题

阅读更多

 一 请求处理的基本过程
  http请求的处理过程

 浏览器或http客户端把 URL(包括post/get提交的内容)经过编码发送给web容器 
 web容器的connector解码URL和其中包含的post/get提交的内容(参数),匹配相应的JSP或Servlet来处理 
 jsp或Servlet处理完毕后,web容器将内容按某种字符集编码返回给浏览器或http客户端 
浏览器或http客户端根据响应头ContentType设置的编码来显示响应

 

一个典型的 URL构成是这样的:

域名:端口/contextPath/servletPath/pathInfo?queryString

说明:

contextPath --web应用的上下文根,也有叫web前缀的, 
servletPath--指在web应用部署描述符web.xml中标签<servlet-mapping>配置的Servlet映射路径

pathInfo--同servletPath一起构成查找jsp或serlvet的路径

queryString--包含请求参数的字符串,以&key=value形式表示参数名(key)及其值(value)

下面分析具体各个部分是怎么处理的:

首先说明几个概念:
编码--将字节转换成字符的过程
解码--将字符转换成字节的过程
URL编码--将URL按照一定的规则转换和编码

URL编码规则是:

字母数字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不变。 
特殊字符 "."、"-"、"*" 和 "_" 保持不变。 
空格字符 " " 转换为一个加号 "+"。 
所有其他字符(包括中文)都是不安全的,因此首先使用一些编码机制(字符集)将它们转换为一个或多个字节。然后每个字节用一个包含 3 个字符的字符串 "%xy" 表示,其中 xy 为该字节的两位十六进制表示形式。 
二 浏览器(http客户端)的处理
下面以比较主流的浏览器IE和FireFox为例来说明,因为本文讨论中文字符问题,均以中文浏览器为例:

1、GET方式提交,浏览器会对URL进行URL encode,然后发送给服务器。
(1) 对于中文IE,如果在高级选项中选中总以UTF-8发送(默认方式),则PathInfo是URL Encode是按照UTF-8编码,QueryString是按照当前浏览器编码(最开始一般为GB2312(IE)或GBK(FireFox))编码。
比如:http://localhost:8080/example/中国?name=中国
实际上提交是:
GET /example/%E4%B8%AD%E5%9B%BD?name=%D6%D0%B9%FA

(2) 对于中文IE,如果在高级选项中取消总以UTF-8发送,则PathInfo和QueryString是URL encode按照GB2312编码。
实际上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA

(3) 对于中文firefox,早期版本的FireFox,pathInfo和queryString都是URL encode按照GBK编码。
实际上提交是:
GET /example/%D6%D0%B9%FA?name=%D6%D0%B9%FA

现在版本(3.0以上)pathInfo也是缺省也按UTF-8发送,QueryString按GBK编码,这个URL编码设置可通过在浏览器地址栏输入about:config,搜索network.standard-url.encode-utf8来设置

很显然,不同的浏览器以及同一浏览器的不同设置,会影响最终URL中PathInfo的编码。对于中文的IE和FIREFOX都是采用GBK编码QueryString。 如果用iso-8859-1来编码发送,无论是PathInfo还是QueryString,如果其中含有中文,毫无疑问,中文字符损失了.损失的意思就是无论再怎么转码和编解码,中文字符再也恢复不过来了。因为中文是按两个(GBK)或多个字节(UTF-8)编码,iso-8859-1是单字节字符集,并不包括中文字符,对于多字节中文 ,iso-8859-1 编码是按单字节编码,这样多字节中文被拆分成单字节进行编码,当遇到iso-8859-1字符集中没有包括的,只能以3f(对应字符?号)代替,因而中文字符最终显示是几个?号,造成乱码.同理,当使用iso-8859-1解码用多字节字符集编码过的含中文字符的字节序列时,也是按单个字节解码,遇到字节(或者说字节对应的编号)在iso-8859-1字符集没有包括的,只能用?号代替

 

[c-sharp] view plaincopy
  1. String str="中文";  
  2.   
  3. 用str.getBytes("iso-8859-1");--中文字符损失了  
  4.   
  5. 用String newStr=new String(str.getBytes("GBK"),"iso-8859-1");  
  6.   
  7. 中文字符还有救,通过new String(newStr.getBytes("iso-8859-1"),"GBK");中文字符转回来了  

 

 

不同http客户端有不同的实现和配置,具体情况具体对待,这里不做分析.

这里还要提到一点:对于 html中超级链接<a href="url" >形式的请求,浏览器也是按GET方式提交的,跟表单(Form)方式的GET请求还是有点细微差别,中文IE并不对QueryString进行URL encode,而是直接进行encode,中文FireFox则是进行URL encode

2.POST方式提交

 PathInfo部分编码参照GET方式,这里没有QueryString了,如果有按GET方式处理,参数部分是在请求体(request body)中按当前浏览器编码传送给web容器的,中文IE和FireFox都是如此

3.浏览器对web容器响应的处理

   浏览器根据web容器响应头Content-Type中charset设置的编码来显示响应内容,并设置为当前浏览器的编码,注意这也是下次请求时的编码,可通过浏览器菜单栏-查看-编码来查看当前浏览器设置的编码

  综合浏览器对请求和响应的处理,可看出Web应用和Web容器在处理编码时,请求和响应的编码设置为一致的是最为科学的.

 

 

三 web容器的处理
 http请求的处理:

1.PathInfo的处理

   无论是get方式还是post方式的请求,web容器在对PathInfo进行url 解码的方式是一致的,web容器提供参数URIEncoding来解码PathInfo,这个参数的缺省值就是UTF-8,这也是跟主流浏览器缺省的URL发送编码为UTF-8是吻合的.

 在http connector接收到浏览器或http客户端发来的请求后,需将请求进行URL解码来交给web容器匹配相应的jsp或servlet来处理,具体解码的过程是在org.apache.coyote.tomcat5.CoyoteAdapter类的postParseRequest()方法中,先进行URL解码,去除URL中的"%","/"以及"+"字符,取出URL 中的有效字符,再根据URIEncoding参数设置的字符集来将URL中的字符进行解码, 在web容器中的每个请求对象中org.apache.coyote.tomcat5.CoyoteRequest对象中均持有一个org.apache.tomcat.util.buf.UDecoder对象的引用,调用其convert()方法用于URL初步解码,调用CoyoteRequest 的convertURI()方法进行有效字符的解码,从而完成整个URL解码的过程

2.请求参数的处理

  请求参数包括get方式提交的QueryString 和post方式提交的在请求体中发送的参数

  请求参数的解析并解码并不发生在请求被http connector接受和处理后,而发生在某次请求,web应用在jsp或Servlet程序中调用了HttpServletRequest对象(web容器含有具体的请求对象实现)的getParameter(),getParameterNames()和getParameterValues()方法中的任意一个,在此之前,请求参数只是作为未解码的字节数组而存在.对于该次请求来说,请求参数的解析并解码仅发生一次.

  解析并解码具体过程:

 这个过程发生在org.apache.coyote.tomcat5.CoyoteRequest的parseRequestParameters()方法中

 (1)通过调用getCharacterEncoding()获取当前请求对象设置的编码字符集,getCharacterEncoding()里获取编码字符集的逻辑对QueryString 的解码起着至关重要的作用,如果编码字符集不对,会直接造成QueryString中文解码错误而最终乱码 ,后面会详细介绍。

 (2)设置参数解码的字符集,调用org.apache.coyote.tomcat5.Parameters对象的setEncoding()和setQueryStringEncoding()方法,其中setEncoding()设置编码字符集的作用于POST方式提交的参数的解码,setQueryStringEncoding()设置的编码字符集作用于GET方式提交的参数的解码.值得一提的是在CoyoteAdapter的service方法中曾有req.getParameters().setQueryStringEncoding(connector.getURIEncoding());用URIEncoding设置编码字符集,这个是没有意义的,最终在这儿会覆盖前面的设置

   如果getCharacterEncoding()获取编码为null,即没有设置编码字符集,使用web容器缺省编码iso-8859-1,因此如果QueryString含有中文,必须保证 getCharacterEncoding()获取到的不是null或iso-8859-1,否则中文字符必乱无疑

 (3)调用handleQueryParameters()进行QueryString参数解析和解码 ,在这里,根据前面获取的字符集,对参数名和值进行解码操作


 (4)读取请求体,并解析和解码参数.处理方式和第(3)步是一样的,这一步只对POST提交的请求有效,GET方式提交的请求在第(3)步已经返回, 如果使用了getReader()或getInputstream()方法读取POST请求,这一步也不会执行 .(这是Servlet规范)

 

既然getCharacterEncoding()至关重要, 下面详细分析获取请求对象编码的逻辑getCharacterEncoding():

获取请求对象编码的逻辑:

(1) 检查当前请求对象charEncoding(代表编码的字符集)是否已经有值,(有可能之前调用过setCharacterEncoding()设置过编码),直接返回charEncoding,如果在这之前已经解析过当前的charEncoding(有可能为null),标记charEncodingParsed的为true的情况,也直接返回charEncoding

(2)如果(1)没有直接返回,尝试从请求头ContentType中的 charset对应的值获取编码字符集设置为charEncoding,置charEncodingParsed标志为true

   如果charEncoding不为null,直接返回charEncoding

(3) 如果(2)中charEncoding为null,尝试从web应用的自定义部署描述符文件tongweb-web.xml中获取编码

    a.首先从隐藏字段获取编码字符集,这个隐藏字段的名字在标签<parameter-encoding>中的属性form-hint-field定义,web应用需要在jsp或这html中的Form表单中加上该隐藏字段确定请求编码的字符集加以提交,或者直接通过在URL 中QueryString中加上该隐藏字段以GET方式提交

    b.如果从隐藏字段获取编码仍为null,使用标签parameter-encoding中的属性default-charset定义的编码

    c.如果从default-charset获得的编码仍然是null,从标签<locale-charset-map>中获取Locale对应编码,比如zh-cn对应的编码字符集就是GBK

    d.如果以上最终得到的 编码不为null,调用setCharacterEncoding()设置请求对象的编码


    从以上web容器处理的逻辑可知,web应用设置请求对象编码有以下方式:

 显示的调用setCharacterEncoding() 
 在自定义部署描述符文件中定义标签<parameter-encoding>的属性form-hint-field,并且使用这个属性定义的名字作为隐藏字段,POST提交或GET提交设置请求对象编码 
在自定义部署描述符文件中定义标签<parameter-encoding>的属性default-charset设置请求对象编码,这个只有在form-hint-field无效时可用 
在自定义部署描述符文件中定义标签<locale-charset-map>,这个只有在default-charset无效时可用 
    优先级是从上到下 ,而且前两种作用于某次请求,后两种是全局性的,在整个web应用中任何一次请求都有效


3.应答的处理

  web容器使用当前应答对象Response设置的编码字符集(可通过调用Response对象的setCharacterEncoding()设置)来将经过jsp或Servlet处理的内容编码成字节数组准备发送给web容器,如果没有设置setCharacterEncoding,缺省使用iso-8859-1 
 web应用中如果没有设置应答(Response)对象的应答头ContentType或者没有设置ContentType中的charset,web容器使用缺省的ContentType,其中charset值是iso-8859-1,浏览器使用此字符集来解码web容器发送的应答内容 
从以上分析可看出,将应答对象的编码设置成ContentType中charset指定的编码以致是不会乱码的,同时应答内容如果含有中文的话,不要使用缺省编码iso-8859-1

 

 

 

4.forward/included,重定向的请求编码

included请求

included请求是在一个jsp中include另一个jsp内容,在include另一个jsp可传递参数,一般使用类似以下jsp标签include另一个请求

<jsp:include flush="true" page="中国.jsp">
 <jsp:param name="name" value="中国"/>
 <jsp:param name="title" value="中文"/>
</jsp:include>

其中<jsp:param>代表传递的参数名及其值

Web容器在将include的jsp转化成Servlet时,对于include部分会按下面部分生成代码:

org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "中国.jsp" + (("中国.jsp").indexOf('?')>0? '&': '?') + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("name", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("中国", request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("title", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("中文", request.getCharacterEncoding()), out, true);

从以上代码可看出,访问included的jsp也是以一个请求URL的形式存在,参数部分按QueryString来传递.<jsp:param>的参数名值已经进行了URL编码,编码采用的字符集就是从请求对象getCharacterEncoding()获取的编码,getCharacterEncoding()的逻辑前面已经详细介绍,可见请求对象的编码字符集也影响到included请求的参数,但是PathInfo部分并未做URL编码,这是因为Web容器在处理include请求时,不再经过Connector部分进行URIEncoding 的解码。因此如果PathInfo部分含有中文字符,要保证不乱码,从而能访问到included的jsp,需保证在jsp在转换成Servlet时并编译成class文件时没有乱码,这就要正确设置jsp的pageEncoding或contentType中charSet,以及编辑jsp文件的IDE使用的编码,Java虚拟机编译时的编码,这些编码字符集保持一致是最好的,当然不能是单字节字符集iso-8859-1

也可以在Servlet中include另一个请求,代码类似这样的 request.getRequestDispatcher("中国.jsp?name=中国&title=中文").include(request,response);

这时PathInfo和QueryString都没有进行过 URL编码,这时跟上面PathInfo的处理一样,必须Servlet在编译成class文件时没有乱码

forward请求

forward请求是在一个jsp中forward到另一个jsp,最终发送给浏览器的是最后一个jsp的内容,跟include一样在另一个jsp可传递参数,一般使用类似以下jsp标签forward到另一个请求

<jsp:forward page="中国.jsp">
   <jsp:param name="name" value="中国.jsp"/>
    <jsp:param name="title" value="中文"/>
</jsp:forward>

Web容器在forward的前一个jsp转化成Servlet时,对于forward部分会按下面部分生成代码:

 _jspx_page_context.forward("中国.jsp" + (("中国.jsp").indexOf('?')>0? '&': '?') + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("name", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("中国.jsp", request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("title", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("中文", request.getCharacterEncoding()));

也可以在Servlet中forward到另一个请求,代码类似这样的 request.getRequestDispatcher("中国.jsp?name=中国&title=中文").forward(request,response);

从以上代码看出forward请求在URL编码上的处理更Include请求是一样的

重定向

重定向指的是从当前请求转向到另一个请求,按照http协议,web容器和浏览器交互过程是这样的:

web容器设置应答状态码为302 
web容器设置应答头Location 及其值(重定向请求的url) 
将应答状态码和应答头发送给浏览器 
浏览器按照状态码302确认为重定向请求,并按应答头Location中的url向web容器发送新的请求 
 这个过程中存在新的 URL,如果其中含有中文,按照Glassfish现有的代码实现,在设置应答头时,直接将一个中文字符强制转换成一个字节,这会造成中文字符损失以致乱码,也就是说在发送给浏览器之前已经乱了,从而重定向请求失败,Tomcat6也有此问题,这方面weblogic9.2中文版有好的表现,没有设置任何参数,用GBK进行了编码,对于重定向中文URL处理得较好,但是如果URL中PathInfo 部分也含有中文字符,不同浏览器却有不同的表现,中文 IE对于应答头Location,将URL是按照PathInfo用UTF-8进行URL编码,其他部分不变,而中文FireFox将 URL中 PathInfo和QueryString都用GBK做了URL编码,按照前面所述,如果 URIEncoding一直为UTF-8(这个值相对比较稳定,不会常修改),这会导致在中文Firefox重定向到一个URL时会访问失败,报404错误

Cookie的编解码

含有Cookie的请求时web容器和浏览器的交互过程:

  web 应用中调用应答对象(Response)的addCookie()方法设置Cookie 
  web容器设置应答头"Set-Cookie" , 
 web容器在发送应答前将应答头发送给浏览器 
浏览器根据应答头"Set-Cookie"在浏览器端创建Cookie 
下次请求时浏览器将Cookie作为请求头"Cookie"发送给web容器 
服务端web应用程序调用请求对象(Request)的getCookies ()方法时,会解析 Cookies,对于该次请求来说,仅仅一次,下次直接获得 
   当然,如果是通过其他途径创建的Cookie,比如JavaScript程序创建的Cookie,只存在浏览器发送Cookie请求头的步骤

从以上的交互过程看出,Cookie也是作为请求头/应答头在浏览器和web容器之间传递的,同重定向一样,Cookie的处理也存在编解码的过程,幸运的是,Glassfish在自定义部署表述符文件中提供标签encodeCookies,容许对Cookie中的名称和值进行URL编码,而tomcat如果 Cookie中含有中文字符,

addCookie()时直接抛出异常,weblogic虽不抛出异常,但是会乱码,Glassfish在Cookie的创建和解析时均可以用UTF-8对Cookie进行编解码,字符集为UTF-8是写死的。

分享到:
评论

相关推荐

    解析WEB开发编码问题

    在Web开发中,编码问题至关重要,因为它涉及到如何正确地处理和传输文本信息。计算机系统处理数据的基本单位是字节,而人类理解的是字符串,这就需要字符集来建立两者之间的桥梁。字符集定义了每个字符如何映射为...

    web 编码问题

    在IT行业中,编码问题是一个广泛且重要的主题,尤其是在Web开发领域。"web编码问题"主要涉及到前端和后端之间的数据交换、字符集处理、URL编码、JavaScript编码规范以及跨平台兼容性等方面。以下是对这些关键知识点...

    PB如何提交WEB请求包含中文参数的解决方案全过程.docx

    PB 是 PowerBuilder 的缩写,这是一种流行的开发工具,但是在提交 WEB 请求包含中文参数时,会出现乱码问题。本文将详细介绍如何解决这个问题,包括使用 GETURL 和 POSTURL 方式的尝试,以及最终的解决方案。 问题...

    解决HTTP请求中的字符编码问题:技术指南与最佳实践

    HTTP作为Web通信的基础协议,其请求中的字符编码问题尤为重要。本文将深入探讨HTTP请求中字符编码的处理方法,包括字符编码的基本概念、常见问题、HTTP头部字段的作用,以及如何在客户端和服务器端处理字符编码。 ...

    web编码问题小结.doc

    本文档《web编码问题小结》详细总结了Java开发中可能遇到的各种编码问题及其解决方案,覆盖了数据库、Cookie、静态页面、POST与GET请求等多种场景。 #### 数据库的中文问题 数据库的中文问题主要源于数据库与应用...

    安全开发之Java Web安全编码.pdf

    ### 安全开发之Java Web安全编码 #### 一、Web应用安全威胁 在现代互联网环境中,Web应用程序作为企业对外展示的重要窗口,面临着各种各样的安全威胁。这些威胁不仅包括了传统的技术层面的问题,还涉及到了更为...

    javaweb全站请求编码过滤器

    在Java Web开发中,"全站请求编码过滤器"是一个至关重要的组件,它确保了所有来自客户端的请求数据(如表单提交、URL参数等)能够正确地被解码为服务器端可理解的格式,避免因为字符编码问题导致的数据乱码。...

    web 请求及http请求报文

    Web请求和HTTP请求报文是网络编程中的核心概念,尤其在开发Web应用程序时至关重要。HTTP(超文本传输协议)是互联网上应用最广泛的一种网络协议,用于从万维网服务器传输超文本到本地浏览器的传输协议。在此,我们将...

    web安全前端编码规范1

    - 使用`:v`进行URL编码,部分模板引擎可能不支持,需注意兼容性问题。 - 使用`:u`对URL参数进行GBK编码。 2. 非CTPL模板: - 对于HTML环境,需要手动转义 `、`&gt;`、`'`、`"`、`\`、`/`、`\n` 和 `\r`。 - 对于...

    C++提交WEB请求(带中文参数)的动态链接库DLL源码

    在IT行业中,C++是一种强大的系统级编程语言,常...开发者在使用这个库时,需要根据自己的需求,将具体的WEB请求地址和参数传递给DLL,然后获取服务器的响应。理解这些知识点对于进行系统集成和跨平台通信非常重要。

    web开发编码设置(防止中文乱码)

    // 设置请求编码 request.setCharacterEncoding("UTF-8"); // 设置响应内容类型及编码 response.setContentType("text/html;charset=UTF-8"); // 处理逻辑 } ``` - **`request.setCharacterEncoding("UTF-8...

    深入分析Java中的中文编码问题

    本文旨在详细探讨Java中的中文编码问题,包括常见的编码格式、编码问题的根源以及如何在Java Web应用程序中妥善处理这些问题。 #### 二、Java中的编码问题概述 ##### 2.1 几种常见的编码格式 在讨论Java中的编码...

    Web应用高级问题

    在JSP页面中,我们需要确保两个关键地方的编码设置一致:页面编码和请求编码。 页面编码是指JSP文件本身的编码格式,它告诉服务器如何解析文件中的字符。在JSP页面的顶部,我们可以看到这样的指令标签`*" ...

    过滤器实现get请求乱码问题

    在这个场景下,我们需要创建一个专门的过滤器来解决GET请求的编码问题。 首先,我们需要了解HTTP协议的基本知识。HTTP请求的GET方法是通过URL来传递参数的,这些参数与URL本身一起被编码。默认情况下,浏览器使用...

    web浏览器发送请求的过程解析--防止乱码的产生

    Web浏览器发送请求的过程是B/S(Browser/Server)架构下的基础,涉及到HTTP协议以及编码问题。HTTP协议是网络上客户端(浏览器)与服务器之间通信的标准,对于理解和处理Web应用程序的国际化至关重要。 首先,HTTP...

    Web开发编码助手++++

    在Web开发过程中,编码与解码是至关重要的环节,它们涉及到数据传输、页面展示和用户交互等多方面。...在实际开发中,掌握这些基础知识和使用工具,能够有效提高开发效率,避免因编码问题引发的诸多错误。

    cxf使用中编码问题

    当我们处理中文或者其他非ASCII字符时,编码问题可能会成为一个棘手的问题。"cxf使用中编码问题"这个主题聚焦在如何在CXF环境中解决与GBK等特定编码相关的困扰。 首先,我们需要理解编码的本质。在计算机系统中,...

    解决boa服务器get/post请求中文乱码问题

    在IT行业中,服务器管理和Web应用开发常常会遇到字符编码的问题,特别是当涉及到非英文字符时。...同时,利用如`decodeURL.c`和`encodeURL.c`这样的工具函数可以有效地处理URL编码问题,从而避免中文乱码。

    get请求乱码问题 java

    1. **检查编码设置**:确保Web服务器(如Apache、Nginx)和应用服务器(如Tomcat)的配置中都设置了正确的字符编码。 2. **统一编码标准**:在项目中使用统一的编码标准,如在JSP页面中添加`; charset=UTF-8" ...

Global site tag (gtag.js) - Google Analytics