【基礎進階】URL詳解與URL編碼

做爲前端,每日與 URL 打交道是必不可少的。可是也許天天只是單純的用,對其只是只知其一;不知其二,隨着工做的展開,我發如今平常抓包調試,接口調用,瀏覽器兼容等許多方面,不深刻去理解URL與URL編碼則會踩到不少坑。故寫下此篇文章,詳解一下 URL 。javascript

   URL 與 URI

不少人會混淆這兩個名詞。html

URL:(Uniform/Universal Resource Locator 的縮寫,統一資源定位符)。前端

URI:(Uniform Resource Identifier 的縮寫,統一資源標識符)。java

關係:瀏覽器

URI 屬於 URL 更低層次的抽象,一種字符串文本標準。tomcat

就是說,URI 屬於父類,而 URL 屬於 URI 的子類。URL 是 URI 的一個子集。服務器

兩者的區別在於,URI 表示請求服務器的路徑,定義這麼一個資源。而 URL 同時說明要如何訪問這個資源(http://)。網絡

 

   端口 與  URL標準格式

何爲端口?端口(Port),至關於一種數據的傳輸通道。用於接受某些數據,而後傳輸給相應的服務,而電腦將這些數據處理後,再將相應的回覆經過開啓的端口傳給對方。dom

端口的做用:由於 IP 地址與網絡服務的關係是一對多的關係。因此實際上因特網上是經過 IP 地址加上端口號來區分不一樣的服務的。
函數

端口是經過端口號來標記的,端口號只有整數,範圍是從0 到65535。

 

URL 標準格式

一般而言,咱們所熟悉的 URL 的常見定義格式爲:

scheme://host[:port#]/path/.../[;url-params][?query-string][#anchor]

scheme //有咱們很熟悉的http、https、ftp以及著名的ed2k,迅雷的thunder等。
host   //HTTP服務器的IP地址或者域名
port#  //HTTP服務器的默認端口是80,這種狀況下端口號能夠省略。若是使用了別的端口,必須指明,例如tomcat的默認端口是8080 http://localhost:8080/
path   //訪問資源的路徑
url-params  //所帶參數 
query-string    //發送給http服務器的數據
anchor //錨點定位

 

   利用 <a> 標籤自動解析 url

開發當中一個很常見的場景是,須要從 URL 中提取一些須要的元素,譬如 host 、 請求參數等等。

一般的作法是寫正則去匹配相應的字段,固然,這裏要安利下述這種方法,來自 James 的 blog,原理是動態建立一個 a 標籤,利用瀏覽器的一些原生方法及一些正則(爲了健壯性正則仍是要的),完美解析 URL ,獲取咱們想要的任意一個部分。

代碼以下:

// This function creates a new anchor element and uses location
// properties (inherent) to get the desired URL data. Some String
// operations are used (to normalize results across browsers).

function parseURL(url) {
    var a =  document.createElement('a');
    a.href = url;
    return {
        source: url,
        protocol: a.protocol.replace(':',''),
        host: a.hostname,
        port: a.port,
        query: a.search,
        params: (function(){
            var ret = {},
                seg = a.search.replace(/^\?/,'').split('&'),
                len = seg.length, i = 0, s;
            for (;i<len;i++) {
                if (!seg[i]) { continue; }
                s = seg[i].split('=');
                ret[s[0]] = s[1];
            }
            return ret;
        })(),
        file: (a.pathname.match(/([^/?#]+)$/i) || [,''])[1],
        hash: a.hash.replace('#',''),
        path: a.pathname.replace(/^([^/])/,'/$1'),
        relative: (a.href.match(/tps?:\/[^/]+(.+)/) || [,''])[1],
        segments: a.pathname.replace(/^\//,'').split('/')
    };
}

Usage 使用方法:

var myURL = parseURL('http://abc.com:8080/dir/index.html?id=255&m=hello#top');
 
myURL.file;     // = 'index.html'
myURL.hash;     // = 'top'
myURL.host;     // = 'abc.com'
myURL.query;    // = '?id=255&m=hello'
myURL.params;   // = Object = { id: 255, m: hello }
myURL.path;     // = '/dir/index.html'
myURL.segments; // = Array = ['dir', 'index.html']
myURL.port;     // = '8080'
myURL.protocol; // = 'http'
myURL.source;   // = 'http://abc.com:8080/dir/index.html?id=255&m=hello#top'

利用上述方法,便可解析獲得 URL 的任意部分。

 

   URL 編碼

爲何要進行URL編碼?一般若是同樣東西須要編碼,說明這樣東西並不適合直接進行傳輸。

一、會引發歧義:例如 URL 參數字符串中使用 key=value 這樣的鍵值對形式來傳參,鍵值對之間以 & 符號分隔,如 ?postid=5038412&t=1450591802326,服務器會根據參數串的 & 和 = 對參數進行解析,若是 value 字符串中包含了 = 或者 & ,如寶潔公司的簡稱爲P&G,假設須要當作參數去傳遞,那麼可能URL所帶參數可能會是這樣 ?name=P&G&t=1450591802326,由於參數中多了一個&勢必會形成接收 URL 的服務器解析錯誤,所以必須將引發歧義的 & 和 = 符號進行轉義, 也就是對其進行編碼。

二、非法字符:又如,URL 的編碼格式採用的是 ASCII 碼,而不是 Unicode,這也就是說你不能在 URL 中包含任何非 ASCII 字符,例如中文。不然若是客戶端瀏覽器和服務端瀏覽器支持的字符集不一樣的狀況下,中文可能會形成問題。

 

那麼如何編碼?以下:

   escape 、 encodeURI 、encodeURIComponent 

escape()

首先想聲明的是,W3C把這個函數廢棄了,身爲一名前端若是還用這個函數是要打臉的

escape只是對字符串進行編碼(而其他兩種是對URL進行編碼),與URL編碼無關。編碼以後的效果是以 %XX 或者 %uXXXX 這種形式呈現的。它不會對 ASCII字符、數字 以及 @ * / + 進行編碼。

根據 MDN 的說明,escape 應當換用爲 encodeURI 或 encodeURIComponent;unescape 應當換用爲 decodeURI 或 decodeURIComponent。escape 應該避免使用。舉例以下:

encodeURI('https://www.baidu.com/ a b c')
// "https://www.baidu.com/%20a%20b%20c"
encodeURIComponent('https://www.baidu.com/ a b c')
// "https%3A%2F%2Fwww.baidu.com%2F%20a%20b%20c"

//而 escape 會編碼成下面這樣,eocode 了冒號卻沒 encode 斜槓,十分怪異,故廢棄之
escape('https://www.baidu.com/ a b c')
// "https%3A//www.baidu.com/%20a%20b%20c" 

encodeURI()

encodeURI() 是 Javascript 中真正用來對 URL 編碼的函數。它着眼於對整個URL進行編碼。

encodeURI("http://www.cnblogs.com/season-huang/some other thing");
//"http://www.cnblogs.com/season-huang/some%20other%20thing";

編碼後變爲上述結果,能夠看到空格被編碼成了%20,而斜槓 /冒號 : 並無被編碼。

是的,它用於對整個 URL 直接編碼,不會對 ASCII字母 、數字 、 ~ ! @ # $ & * ( ) = : / , ; ? + ' 進行編碼。

encodeURI("~!@#$&*()=:/,;?+'")
// ~!@#$&*()=:/,;?+'

encodeURIComponent()

嘿,有的時候,咱們的 URL 長這樣子,請求參數中帶了另外一個 URL :

var URL = "http://www.a.com?foo=http://www.b.com?t=123&s=456";

直接對它進行 encodeURI 顯然是不行的。由於 encodeURI 不會對冒號 : 及斜槓 / 進行轉義,那麼就會出現上述所說的服務器接受到以後解析會有歧義。

encodeURI(URL)
// "http://www.a.com?foo=http://www.b.com?t=123&b=456"

這個時候,就該用到 encodeURIComponent() 。它的做用是對 URL 中的參數進行編碼,記住是對參數,而不是對整個 URL 進行編碼。

由於它僅僅不對 ASCII字母、數字 ~ ! * ( ) '  進行編碼。

錯誤的用法:

var URL = "http://www.a.com?foo=http://www.b.com?t=123&s=456";
encodeURIComponent(URL);
// "http%3A%2F%2Fwww.a.com%3Ffoo%3Dhttp%3A%2F%2Fwww.b.com%3Ft%3D123%26s%3D456"
// 錯誤的用法,看到第一個 http 的冒號及斜槓也被 encode 了 

正確的用法:encodeURIComponent() 着眼於對單個的參數進行編碼:

var param = "http://www.b.com?t=123&s=456"; // 要被編碼的參數
URL = "http://www.a.com?foo="+encodeURIComponent(param);
//"http://www.a.com?foo=http%3A%2F%2Fwww.b.com%3Ft%3D123%26s%3D456"

利用上述的使用<a>標籤解析 URL 以及根據業務場景配合 encodeURI() 與 encodeURIComponent() 便可以很好的處理 URL 的編碼問題。

應用場景最多見的一個是手工拼接 URL 的時候,對每對 key-value 用 encodeURIComponent 進行轉義,再進行傳輸。

 

原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。

相關文章
相關標籤/搜索