很多八股文会给出:
- DNS Resolution
- Establishing a Connection
- Sending an Http Request
- Receiving the HTTP Response
- Rendering the Web Page
但今天我斗胆插入第0.9步URL Parsing
,
URL( uniform resource locator)由四部分组成: scheme、domain、path、resource
URL Parsing做了2个事情:
- prase url:只有解析分离出domain,还有后续的第1步:
DNS resolution
- url_encode
本文我主要想聊一聊url_encode
- 为什么会有url_encode?
- javascript
encodeURI()
vsencodeURIComponent()
- 我为什么会想到这个问题?
- 常见的httpclient默认有做url_encode吗?
在浏览器插入https://www.baidu.com/s?wd=博客园马甲哥
,Enter之前童鞋们可尝试拷贝地址栏, 粘贴到任意位置, 内容是:https://www.baidu.com/s?wd=%E5%8D%9A%E5%AE%A2%E5%9B%AD%E9%A9%AC%E7%94%B2%E5%93%A5
, 这就是浏览器自动url_encode
的结果, 浏览器会拿这个网址去做 dns、request行为。
1. 为什么会有url_encode?
url_encode 又叫百分号编码,为什么要有url_encode,看知乎。
总结下来:uri地址最初要求是以可显示、可写的 ascii 字符集, 非英文字符和其他特殊字符需要被编码。
默认按照UTF-8转化为字节流,每个字节按16进制表示,并添加%组成一个percent编码。
例如:汉字 “你好”
- UTF-8字节流打印为:-28 -67 -96 -27 -91 -67
- 对应的16进制表示为:E4 BD A0 E5 A5 BD
- URLEncode编译后为:%E4%BD%A0%E5%A5%BD
当然服务端会对应的url_decode
函数,编码/解码的次数需要对应。
2. js 中的encodeURI()
vs encodeURIComponent()
encodeURI是js 中内置的全局函数,用于url_encode,不会对以下特殊字符编码,这也是为了确保url中原生字符的正确表达:
A–Z a–z 0–9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , #
const uri = 'https://mozilla.org/?x=шеллы';
const encoded = encodeURI(uri);
console.log(encoded);
// Expected output: "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
encodeURIComponent 也是全局函数,但他的用途是对字符串做完整的url_encode, 这个函数会对上面排除的字符做编码,这个函数一般用于已知是特殊字符需要做url编码。
// Encodes characters such as ?,=,/,&,:
console.log(`?x=${encodeURIComponent('test?')}`);
// Expected output: "?x=test%3F"
3.我为什么会关注这个问题?
一般web框架会为我们自动解码,所以我们在直接处理http请求时可以忽略该问题。
但是在自行使用 httpclient反代时就要注意这个问题。
- $request_uri
full original request URI (with arguments) - $uri
current URI in request, normalized,
The value of $uri may change during request processing, e.g. when doing internal redirects, or when using index files.
我在使用lua-resty-http客户端做反向代理请求时遇到了这个问题, 一开始lua_resty_http 将 $uri(解码值)送到应用,不符合应用的预期, 应恢复成原始编码uri。
4. 常见的httpclient是否指出自动url_encode?
.NET、go、lua的HttpClient(包括curl)都不会自动对 URL 进行编码。 如果你的 URL 包含特殊字符或需要编码的字符,你需要自己手动进行 URL 编码。
- [C#] System.Net.WebUtility.UrlEncode
- [golang] url.QueryEscape(rawURL)
- [lua] ngx.escape_uri(str, 0)