要知道,其中包含两种编码,一种是字符到字符的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成了集合,你通过索引号直接取出来用就就行了。