URL的编码和解码
URL的编码和解码
参考:阮一峰--关于URL编码
1 为什么要URL编码
- 在因特网上传送URL,只能采用ASCII字符集
也就是说URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号,即
只有字母和数字[0-9a-zA-Z]、一些特殊符号$-_.+!*'()[不包括双引号]、以及某些保留字(空格转换为+),才可以不经过编码直接用于URL
这意味着 如果URL中有汉字,就必须编码后使用。 但是麻烦的是 标准的国际组织并没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。 这导致"URL编码"成为了一个混乱的领域。
如果包含中文,其实会自动编码的,比如Chrome和火狐,"文"和"章"的utf-8编码分别是"E6 96 87"和"E7 AB A0" ,下图所示的"%e6%96%87%e7%ab%a0"就是按照顺序,在每个字节前加上%而得到的:

但是不同的浏览器可能会有不同的编码方式,不要将编码交给浏览器。应该用JS在前端对URL编码,这样就实现了统一
- 如果key=value这种传参方式中,value中包含
?``=或者&等符号,url的解析会变得很困难 - 不同的操作系统、浏览器、不同的网页字符集(charset)有不同的默认编码方式,要有一个统一格式来发送url,参考文章中举了4个例子(很有读的必要)!
2 如何编码
URL编码通常也被称为百分号编码(percent-encoding),是因为它的编码方式非常简单:
使用%加上两位的字符——0123456789ABCDEF——代表一个字节的十六进制形式。URL编码要做的,就是将每一个非安全的ASCII字符都被替换为“%xx”格式,
对于非ASCII字符,RFC文档建议使用utf-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码。
如"中文"使用UTF-8字符集得到的字节为0xE4 0xB8 0xAD 0xE6 0x96 0x87,经过Url编码之后得到"%E4%B8%AD%E6%96%87"。
一些常见的特殊字符换成相应的十六进制的值:
+ %20
/ %2F
? %3F
% %25
# %23
& %26
2.1 JS的三种编码函数
上面说了编码方式的混乱,那么如何统一呢?
**使用Javascript先对URL编码,或者将可以在后台编码的参数编码后再发送给前端使用。然后再向服务器提交,不要给浏览器插手的机会,这样就能保证客户端只用一种编码方法向服务器发出请求 **
escape
js中编码出生最早的一个,不提倡使用,真正作用是:
返回一个字符的Unicode编码值,为的是方便他们能在所有计算机上可读,规则:
所有空格、标点以及其他非ASCII字符都用%xx编码替换; 例如空格返回的是%20 字符值大于255的字符以%uxxxx格式储存
encodeURI函数(推荐使用)
这个函数才是javascript中真正用来对URL编码的函数
它着眼于对整个URL进行编码,因此除了常见的符号以外,对其他一些在网址中有特殊含义的符号"; / ? : @ & = + $ , #",也不进行编码。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。
需要注意的是,它不对单引号'编码
它对应的解码函数是decodeURI()。
规则就是我上面第二部分所说的,采用utf-8编码。比如:

encodeURIComponent函数(推荐使用)
与encodeURI()的区别是,它用于对URL的组成部分进行个别编码,而不用于对整个URL进行编码。
因此,"; / ? : @ & = + $ , #",这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码,具体的编码规则是和encodeURI函数是一样的
它对应的解码函数是decodeURIComponent()。
实验:
利用chrome的开发者工具:

可以看到第一种,对需要url编码的部分用encodeURIComponent函数,其他部分不编码符合要求,即
对需要编码的参数用encodeURIComponent函数最推荐
2.2 我们的问题
遇到的问题:
get请求的路径参数filePath为:/image/5cf4adbe16ad4fc18ab2259cb86bb14d.png,
在相应的控制器Controller中:
@RequestMapping(path = "/admin/{filePath}")
那么这个请求就变成了:
http://localhost/admin//image/5cf4adbe16ad4fc18ab2259cb86bb14d.png
由于服务器无法解析上面的url,导致400 bad request错误
2.3 Java的URLEncoder.encode("需要编码的参数","UTF-8")
比较JS的encodeURIComponent函数和Java的URLEncoder.encode("需要编码的参数","UTF-8")函数:
对//中国/images/head_tripletown.png//!@#$%^&*()进行URL编码:
//JS的encodeURIComponent函数
javascript:encodeURIComponent("//中国/images/head_tripletown.png//!@#$%^&*()")
"%2F%2F%E4%B8%AD%E5%9B%BD%2Fimages%2Fhead_tripletown.png%2F%2F!%40%23%24%25%5E%26*()"
//Java的URLEncoder.encode("需要编码的参数","UTF-8")函数
URLEncoder.encode("//中国/images/head_tripletown.png//!@#$%^&*()","UTF-8")
%2f%2f%e4%b8%ad%e5%9b%bd%2fimages%2fhead_tripletown.png%2f%2f!%40%23%24%25%5e%26*()
可以看到一模一样,因此:
使用Javascript先对URL编码,或者将可以在后台编码的参数编码后再发送给前端使用。
3 为什么两次编码
首先看例子,原始请求:
http://localhost/admin/image/filePath//images/head_tripletown.png/200/200
其中,Controller中的映射文件:
@RequestMapping(path = "/admin/image/filePath/{filePath}/{width}/{height}")
对filePath参数一次编码后,发起URL请求:
请求为:http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200
在拦截器加断点:

毫无反应。。。所以应该在拦截器工作前就对URL进行了解码
对filePath参数两次编码后,发起URL请求:
请求为:http://localhost/admin/image/filePath/%252fimages%252fhead_tripletown.png/200/200
在拦截器加断点:

一次解码之前:

一次解码之后:

获得了正常回应:

两次编码的原因:
- 一般的原因:解决服务器解码后乱码问题
如果只进行一次encodeURI,得到的是UTF-8形式的URL,服务器端通过request.getParameter()解码查询参数(通常是iso-8859-1)就会得到乱码。
如果进行两次encodeURI,第一次编码得到的是UTF-8形式的URL,第二次编码得到的依然是UTF-8形式的URL,但是在效果上相当于首先进行了一次UTF-8编码(此时已经全部转换为ASCII字符),再进行了一次iso-8859-1编码,因为对英文字符来说UTF-8编码和ISO-8859-1编码的效果相同。在服务器端,首先通过request.getParameter()自动进行第一次解码(可能是gb2312,gbk,utf-8,iso-8859-1等字符集,对结果无影响)得到ascii字符,然后再使用UTF-8进行第二次解码,通常使用java.net.URLDecoder("","UTF-8")方法。
两次编码两次解码的过程为:
UTF-8编码->UTF-8(iso-8859-1)编码->iso-8859-1解码->UTF-8解码,编码和解码的过程是对称的,所以不会出现乱码。
- 我们的原因:解决400 bad request错误
由于我们发送的请求为:
http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200
服务器端首先进行一次解码,变为:
http://localhost/admin/image/filePath//images/head_tripletown.png/200/200
在dispatcherservlet(前端控制器,用来查询映射文件,转发请求和转发回应)中查询映射文件,发现没有匹配的RequestMapping,就会报400 bad request错误
如果两次编码:
http://localhost/admin/image/filePath/%252fimages%252fhead_tripletown.png/200/200
服务器端首先进行一次解码,变为:
http://localhost/admin/image/filePath/%2fimages%2fhead_tripletown.png/200/200
查询映射文件可以正常转发,在接收请求后在手动进行一次解码。
4 扩展
什么是application/x-www-form-urlencoded
它是一种编码类型。当URL地址里包含非西欧字符的字符串时,系统会将这些字符转换成application/x-www-form-urlencoded字符串。表单里提交时也是如此,当包含非西欧字符的字符串时,系统也会将这些字符转换成application/x-www-form-urlencoded字符串,然后在服务器端自动解码。FORM元素的enctype属性指定了表单数据向服务器提交时所采用的编码类型,默认的缺省值是“application/x-www-form-urlencoded。
然而,在向服务器发送大量的文本、包含大量非ASCII字符的文本或二进制数据时这种编码方式效率很低。这个时候我们就要使用另一种编码类型“multipart/form-data”,比如在我们在做上传的时候,表单的enctype属性一般会设置成“multipart/form-data”。 Browser端<form>表单的ENCTYPE属性值为multipart/form-data,它告诉我们传输的数据要用到多媒体传输协议,由于多媒体传输的都是大量的数据,所以规定上传文件必须是post方法,<input>的type属性必须是file。
URL的编码和解码的更多相关文章
- url在线编码和解码
在工作中,经常遇到encode之后的url.想查看里面的某个参数的时候,很不直观.今天在网上搜了一下对url在线编码和解码的网站.对我来说,使用起来很方便.而且这个网站里面,不仅仅有对url的编码和解 ...
- java中URL 的编码和解码函数
java中URL 的编码和解码函数java.net.URLEncoder.encode(String s)和java.net.URLDecoder.decode(String s);在javascri ...
- javascript对url进行编码和解码
这里总结下JavaScript对URL进行编码和解码的三个方法. 为什么要对URL进行编码和解码 只有[0-9[a-Z] $ - _ . + ! * ' ( ) ,]以及某些保留字,才能不经过编码直接 ...
- .NET Core中如何对Url进行编码和解码
我们在.NET Core项目中,可以用WebUtility类对Url进行编码和解码,首先我们要确保项目中引入了nuget包:System.Runtime.Extensions 当然这个nuget包默认 ...
- 在线url网址编码、解码
>>在线url网址编码.解码<<
- JS对url进行编码和解码(三种方式区别)
Javascript语言用于编码的函数,一共有三个,最古老的一个就是escape().虽然这个函数现在已经不提倡使用了,但是由于历史原因,很多地方还在使用它,所以有必要先从它讲起. escape 和 ...
- URL地址编码和解码
0. 参考 [整理]关于http(GET或POST)请求中的url地址的编码(encode)和解码(decode) python3中的urlopen对于中文url是如何处理的? 中文URL的编码问题 ...
- js 对url进行编码和解码的三种方式
一.escape 和 unescape escape 原理:对除 ASCII字母.数字.标点符号(@ * _ + - . /) 以外的字符进行编码 .编码的字符被替换成了十六进制的转义序列 不编码的字 ...
- js对url进行编码和解码
编码 只有 0-9[a-Z] $ - _ . + ! * ' ( ) , 以及某些保留字,才能不经过编码直接用于 URL. 例如:搜索的中文关键字,复制网址之后再粘贴就会发现该URL已经被转码. 1. ...
随机推荐
- 201521123068 《java程序设计》 第10周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...
- 201521123092《java程序设计》第14周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...
- 201521044091 java 第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. Runnable不是线程,Thread才是,必须将实现Runnable接口的类的对象放入Thread中才能在 ...
- java面试题整理(1)
1.Equals与==的区别? ==是判断两个变量或者实例是不是指向同一个内存地址 equals是判断两个变量或者实例所指向的内存地址中的值是不是相同 2.Object有哪些公用方法? 方法equal ...
- [PHP源码阅读]number_format函数
上次讲到PHP是如何解析大整数的,一笔带过了number_format的处理,再详细阅读该函数的源码,以下是小分析. 函数原型 string number_format ( float $number ...
- [cocos2dx] lua注册回调到c++
思路 像所有语言一样,绑定回调主要是执行的任务执行到特定情形的时候,调用对用回调方法. 这里也一样.核心思路是,当c代码执行到特定特定情形的时候,调用lua的方法 我这里使用的是用lua_stack直 ...
- 最详细的cookie和浏览隐私之间的关系
本文所说的"cookie",指的是浏览器相关的 cookie(也叫"HTTP cookie"). 浏览器 cookie 的主要功能是:帮助网站保存一些小片段的信 ...
- 都是Javascript的作用域惹得祸
案件重现 今天有位然之OA 系统的定制开发用户咨询了个问题,他想在新加的功能模块的操作面板中,实现用户点击删除按钮时提示友好提醒,如下: 问题很简单,虽然他自己最终达到目的效果了,但不知道起初问题出在 ...
- js中set和get的用法
get 语句作为函数绑定在对象的属性上,当访问该属性时调用该函数. set 语法可以将一个函数绑定在当前对象的指定属性上,当那个属性被赋值时,你所绑定的函数就会被调用. eg: var log = [ ...
- Java常用异常整理
填坑,整理下Java的常用异常.正确使用异常在实际编码中非常重要,但面试中的意义相对较小,因为对异常的理解和应用很难通过几句话或几行代码考查出来,不过我们至少应答出三点:异常类的继承关系.常用异常类. ...