一 请求处理的基本过程
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字符集没有包括的,只能用?号代替
- String str="中文";
- 用str.getBytes("iso-8859-1");--中文字符损失了
- 用String newStr=new String(str.getBytes("GBK"),"iso-8859-1");
- 中文字符还有救,通过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是写死的。
相关推荐
编码问题一直困扰着开发人员,尤其在Java中更加明显,因为Java是跨平台语言,不同平台之间编码之间的切换较多。本文将向你详细介绍Java中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别...
• 网络——你将学到:跨域共享资源、无损压缩图片大小,以及使用块编码加快网页渲染。 • 浏览器——你将发现:避免或取代iframe的方法、简化CSS选择符,以及其他技术。 对于当前的富媒体网站和Web 2.0应用...
本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一...
在JSP 的众多优点之中,其中之一是它能将 HTML 编码从 Web 页面的业务逻辑中有效地分离出来。用 JSP 访问可重用的组件,如 Servlet、JavaBean 和基于 Java 的 Web 应用程序。JSP 还支持在 Web 页面中直接嵌入 Java ...
本谷歌Chrome扩展程序可以帮助开发人员开发和测试REST风格的Web服务API与所有支持的方法,比如GET,POST,PUT,PATCH,DELETE 和 OPTIONS。 该扩展程序支持HTTP基本身份验证,支持多种头部信息和响应格式。 本扩展...
其中包括:请求编码,响应编码,转发,包含,重定向;
物理路径泄露 物理路径泄露一般是由于Web服务器处理用户请求出错导致的,如通过提交一个超长的请求,或者是某个精心构造的特殊请求,或是请求一个Web服务器上不存在的文件。这些请求都有一个共同特点,那就是被请求...
其次深入介绍Java技术,包括I/O技术、中文编码问题、Javac编译原理、class文件结构解析、ClassLoader工作机制及JVM的内存管理等。最后介绍Java服务端技术,主要包括Servlet、Session与Cookie、Tomcat与Jetty服务器、...
其次深入介绍了Java 技术,包括I/O 技术、中文编码问题、Javac 编译原理、class 文件结构解析、ClassLoader 工作机制及JVM 的内存管理等。最后介绍了Java 服务端技术,主要包括Servlet、Session 与Cookie、Tomcat 与...
个人原创并亲自调试成功,通过VBA调用金蝶K3/CLOUD WebAPI读取任意行物料编码和物料名称到Excel表中,VBA无加密,完整源码。
1.请求网址 请求网址输入你在公众平台开发模式下的那个URL(如下图),这个工具不带有验签算法之类的,所以不用填写Token了,但是在WEB程序中为了安全我们是需要验签的,因此,你要加上只有你知道的参数比如debug=...
CHttpClient HttpRequest;... //注意 这里返回的json数据 如果是中文的话,是 编码模式存在的,可以用jsoncppp解析,解析出来默认就是中文。 ::MessageBoxA(NULL,"JSON数据 记事本写入成功" ,"提示",MB_OK);
Unity 中通过UnityWebRequest 以POST形式传JSON格式(键值对格式)的参数请求数据。
Java 的 Web框架虽然各不相同,但基本也都是遵循特定的路数的:使用Servlet或者Filter拦截请求,使用MVC的思想设计架构,使用约定,XML或 Annotation实现配置,运用Java面向对象的特点,面向抽象实现请求和响应的...
其次深入介绍了Java 技术,包括I/O 技术、中文编码问题、Javac 编译原理、class 文件结构解析、ClassLoader 工作机制及JVM 的内存管理等。最后介绍了Java 服务端技术,主要包括Servlet、Session 与Cookie、Tomcat 与...
请求http工具 支持ssl,请求参数支持json数据和map数据,能自动转换编码),不必担心返回数据乱码
关于applicationx-www-form-urlencoded等字符编码的解释说明,挺有用的,如果你开发Restful Web service。
数据包嗅探工具HTTPNetworkSniffer可以捕获所有Web浏览器和Web服务器之间发送的HTTP请求/响应,显示在一个简单的表。对于每一个HTTP请求,将显示以下信息:主机名,HTTP方法(GET,POST,HEAD),URL路径,用户代理...
在Java Web程序开发中,由于Web容器内部使用编码格式并不支持中文字符集,所以,处理浏览器请求中的中文数据就会出现乱码的现象。由于Web容器使用了ISO-8859-1的编码格式,所以在Web应用的业务处理中也会使用ISO-...
传统的Web服务大多采用基于RPC交互模型,该模型在相对... 最后将论文的研究成果应用于直升机数字化工程的信息编码管理系统中,应用表明:基于REST的Web Services可以降低了Web服务的耦合度,提高了系统的可靠性和可伸缩性。