URL编码,客户端和服务器之间发生了什么

标签: url 编码 客户端 | 发表时间:2015-01-13 02:01 | 作者:崔永键
出处:http://www.cuiyongjian.com/


要知道,其中包含两种编码,一种是字符到字符的URL编码(可以理解为仅仅是给字符换了一种字符层面上的表示形式而已,可以理解为转义),一种是字符到二进制的传统意义上我们理解的字符编码。

先来看,服务器到客户端的消息。
服务器端对HTTP头内容,首先进行URL编码,比如其中的汉字,就使用UTF8转换为对应的UTF8编码值。例如“中国”两个字的UTF8编码为e4b8ad e59bbd.
于是,URL编码就把中国变为这样的字符串:"%e4%b8%ad%e5%9b%bd"。
由此可见,在服务器还没有往客户端传输之前,服务器会把所有的HTTP头的内容都变成英文字符,因为汉字啥的全变成%e4这种形式了。这就是字符到字符的URL编码,可以发现他编码前编码后都是字符。
然后,经过第一步以后,既然都是英文字符了,服务器把这一堆代表HTTP头字符串,以ASCII编码的形式转化为字节数组(这次编码是关系到同一个字符的二进制如何进行存储,决定了占用空间的大小),发送给客户端。
比如你设置的http头Content-Type:"text-中国",最终就打包为Content-Type=%e4%b8%ad%e5%9b%bd 这样的字符流,在网络中传输。(当然网络中是这些英文字母的ascii二进制形式进行传输)。 
对比下就能发现,这种把原文转换为ASCII字符的方式,确实增加了存储的代价,增加了网络带宽。因为中国两个字我本来可以用6个字节的UTF8编码存储。可转义后变成%e4%b8%ad%e5%9b%bd,那就是18个字符占用18个字节存储。不过这仅仅是HTTP上关于特殊字符的规定,就不让你用。其实http头占不了多少空间。而且基本都是英文字母来表示,所以要理解他,关心他。
其实也有用处的,比如你表示等号,可是这个符号容易跟http协议里规定的东西产生歧义,所以URL编码之后就变成了%23这样的东西,虽然比直接传输等号多占了2个字节,但是解决了歧义的问题。
在客户端浏览器无论使用get还是post通过http的body体来传输数据,最终都会把客户端的汉字等特殊字符进行转义,我不知道为什么这样设置,这确实增大了带宽负载。这或许就是http的性能略低的原因吧。

客户端浏览器拿到这个HTTP头的时候,就一个一个字节的打开这些东西。按照ASCII码去解析传来的字节流,翻译为字符,最终再次得到了Content-Type=%e4%b8%ad%e5%9b%bd. 此时,还要进行URL反编码,把其中的汉字等特殊符号反编码为原来的字符。(反转义,这就是转义的作用,比如服务器传来了一个真正的等号,和一个%23,那么客户端就能断定真正的等号是http协议要用的,%23是当作一个普通的等号字符翻译出来)

有个问题就是:对于HTTP的响应头来说,浏览器默认认为服务器进行URL编码的时候,不会转义汉字进去。所以他收到Content-Type=%e4%b8%ad%e5%9b%bd的时候,并没有按照汉字的转义方法去反解析他,而是认为%e4是一个字符,结果%e4%b8%ad%e5%9b%bd就变成了??-??? 这样的六个字符东西。


而HTTP的响应body里面,是完全按照你程序员的意思写进去的二进制内容。客户端拿到以后,他会根据你的Content-Type的指定,来调用相关的渲染程序去解析。比如如果是html的话,客户端就看你指定的charset编码(没有指定就按照客户端浏览器设置的来), 他去把网络传输来的http body字节流,按照http头里charset指定的编码方式来反解码,得到一堆html字符。 然后调用渲染程序进行渲染页面。

如果你服务器返回的Content-Type是images,则浏览器就认为你的http body里传来的字节流都是图片的二进制内容。(当然你服务器那边程序员肯定也要传图片的流进来)。结果客户端浏览器就能按照图片的方式把这堆二进制解释出来了。




再来看关于请求消息的编码:
客户端浏览器发起请求的时候,URL会被转义(URL编码)。  比如你通过URL传递参数到服务器。 使用类似于 http://www.baidu.com/index.html?key=中国
。这样的话,浏览器提交的时候,会自动进行特殊字符的URL编码(上文说了,可以理解为转义)。
真正的HTTP请求头,很可能是这样的:
GET %3a%2f%2fHandler1.ashx?t=%E4%B8%AD%E5%9B%BD
连网址的反斜杠都被转义了。
这些URL编码后的字符,都通过网络传输到服务器。
服务器拿到后,将网络传输来的二进制字节流,按照ASCII码反解析为字符文本。结果就是:
GET %3a%2f%2fHandler1.ashx?t=%E4%B8%AD%E5%9B%BD
当然还有其他的一些头。
在ASP.NET里你使用Request对象的RawURL就能看到原始URL信息。
然后,你不能直接用这种转义的字符啊。你还得想办法,把他转成原始的字符。于是你请出了HttpUtiliy.UrlDecode()
就可以把URL编码的文本再返回原貌了。
由于客户端多数浏览器使用的是UTF8对这种URL参数进行的URL编码,所以你也要使用UrlDecode的UTF8方式进行反解析。
当然,ASP.NET的UrlDecode默认就是使用UTF8的。所以,http%3a%2f%2localhost%2ffHandler1.ashx?t=%E4%B8%AD%E5%9B%BD
这句话,就被解析为:http://localhost/Handler1.ashx?t=中国
当然,关于客户端URL参数使用哪种编码方式,标准没有规定,所以不同的浏览器厂商可能会采用不同的方式。有特殊情况话,你在服务器端就要特殊处理了。网上我见过有人处理火狐的情况,我想还是用到的时候再去学习这块内容吧。不过现在浏览器基本都是使用UTF8进行URL编码。


再来看客户端发给服务器消息体的URL编码。(也就是post请求的时候)
用户填写表单,点击提交,浏览器就会自动对用户的表单各个项目进行URL编码。反正就是转义为无歧义,无汉字的一堆字符,全是百分号。
asp.net可以通过Request对象的InputStream拿到这个传输到服务器的客户端http body内容,你需要使用流的Read方法把数据读到字节数组,然后用ASCII编码把这字节数组读成字符串。也就是浏览器传来的那一堆已经URL编码了的文本。

要想看里面用户填写的原始内容,又需要使用HttpUtility.UrlDecode进行反转义。 又要注意编码问题,因为客户端可能使用的非UTF8进行的URL编码。
比如有的火狐浏览器,是根据用户html里的<meat charset> 这个地方来确定传输给服务器时如何进行URL编码。


然而,上文讲了那么多,其实在asp.net里面,只要使用Request.QureyString或 Request.Form拿到d数据,就是由asp.net框架已经给你URL反编码之后的了。而且给你把http报文URL反编码之后还进行split成了集合,你通过索引号直接取出来用就就行了。

相关 [url 编码 客户端] 推荐:

将URL编码?

- - JavaScript - Web前端 - ITeye博客
    URL一般只能由字母、数字、$ - _. * ' ( ) 等一些字符构成. 那么当URL中需要用到汉字时怎么办,譬如有这样的URL: "www.test.com/search?name=张三",此时,只有通过将URL进行编码的方式进行传递了.     Javascript编/解码方法:.     如果对上面的URL(www.test.com/search?name=张三)进行编码的话.

URL编码,客户端和服务器之间发生了什么

- - 崔永键的博客
要知道,其中包含两种编码,一种是字符到字符的URL编码(可以理解为仅仅是给字符换了一种字符层面上的表示形式而已,可以理解为转义),一种是字符到二进制的传统意义上我们理解的字符编码. 先来看,服务器到客户端的消息. 服务器端对HTTP头内容,首先进行URL编码,比如其中的汉字,就使用UTF8转换为对应的UTF8编码值.

理清URL编码

- winners - Thinking for Fun
关于URL编码,RFC1738做了如下的规定:. “Only alphanumerics [0-9a-zA-Z], the special characters “$-_.+!*’(),” [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL.”.

Web开发:URL编码与解码

- - 搜索引擎技术博客
通常如果一样东西需要编码,说明这样东西并不适合传输. 原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义. 例如Url参数字符串中使用key=value键值对这样的形式来传参,键值对之间以&符号分隔,如/s?q=abc&ie=utf-8. 如果你的value字符串中包含了=或者&,那么势必会造成接收Url的服务器解析错误,因此必须将引起歧义的&和=符号进行转义,也就是对其进行编码.

URL的井号

- chenqj - 阮一峰的网络日志
一个显著变化,就是URL加入了"#!"符号. 在我印象中,这是主流网站第一次将"#"大规模用于直接与用户交互的关键URL中. 这表明井号(Hash)的作用正在被重新认识. 本文根据HttpWatch的文章,整理与井号有关的所有重要知识点. 其右面的字符,就是该位置的标识符. 就代表网页index.html的print位置.

iOS UIWebView URL拦截

- - 移动开发 - ITeye博客
本文译者: candeladiao,原文: URL filtering for UIWebView on the iPhone. 说明:译者在做app开发时,因为页面的javascript文件比较大导致加载速度很慢,所以想把javascript文件打包在app里,当UIWebView需要加载该脚本时就从app本地读取,但UIWebView并不支持加载本地资源.

URL最大长度问题

- - CSDN博客推荐文章
这几天为解决一个BUG头疼了一段时间,BUG现象如下:. 一个选择人员的选择控件,当选择多个人时(50多个的时候),返回没有错误现象,而再一次打开的时候就报404错误. 看到这个错误非常纳闷,无法下手,只能再一次看控件的代码,在详细看代码时,发现所有的参数都是经过URL传参的,赶紧百度一下URL参数的大小限制(从这个百度开始,我就进入一个误区:参数大小的限制).

URL中井号的作用

- - CSDN博客Web前端推荐文章
  URL中的井号(#)是比较常见的,它并不影响网址的指向,而是有众多功能和特点的. 下面就为大家介绍一些有关井号的故事.   1、页面中的某一个位置可以用井号在URL中指定.   井号作为比较长出现在URL的一种符号,通常也会代表这个页面中的某一个位置,比如:http://aoshu.juren.com/chzt/xiaoxueshijuan/index.html#nn1,此URL表示在这个页面中nn1的位置.

APP调用Custom URL Scheme

- - IT技术博客大学习
标签:   Scheme   跳转. Custom URL scheme 的好处就是,你可以在其它程序中通过这个url打开应用程序. 如A应用程序注册了一个url scheme:myApp,  那么就在mobile浏览器中就可以通过打开你的应用程序A. 首先在AndroidManifast.xml要被指定Scheme的Activity下设置如下参数.