如何從一個 URL 字符串中提取出origin (scheme+domain+port) 信息?

前言

最近遇到一個需求:用postMessage來實現跨域通訊。
其中有一個注意的地方:即接收方來收到消息時須要對發送方的 origin 進行檢查,若是不是目標發送方,則什麼都不作,這是出於安全考慮推薦的方式。前端

我手中有的數據有:消息事件中的 event.origin(即當前發送方的源)、目標發送方的url字符串。因此爲了進行安全檢查,我得先從目標發送方的url字符串中提取出 origin信息。 正則表達式

那麼問題來了,怎麼提取呢?
——固然用正則表達式最簡單!跨域

origin是什麼

在介紹提取的正則表達式以前,咱們得先搞清楚咱們要提取的是url字符串中的哪一部分的字符。咱們知道要提取的是origin信息,那origin是什麼意思呢?
前端開發常常聽到一個詞「同源策略」origin即爲其中的「源」。一個 URL 具體由哪幾部分組成,能夠參考這篇文章
而 origin = scheme(協議)+ domain(域名)+port(端口)
例如:
url= "http://baidu.com:8080/pub/new";
origin = "http://baidu.com:8080";安全

匹配正則表達式

我想用 RegExp.exec()方法來提取 origin。構造一個RegExp對象有兩種方法:dom

  • RegExp(pattern [, flags]), 如 new RegExp("ab+c", "i");post

  • /pattern/flags, 如 /ab+c/i;url

注意:不少人誤認爲這2種方法的區別只在於前者 pattern 是一個字符串,我本來也是這麼認爲的。結果就被這個潛意識坑得很慘。.net

匹配 origin 的正則 pattern 以下所示,能匹配http/https協議、域名中帶「-」、「_」字符如「dx-meituan.dxw_mei.com」。至於具體如何匹配到每個字符的,請自行參考RegExp對象code

^https?://[\w-.]+(:\d+)?對象

下面將分別用前面介紹的2種方法建立 RegExp 對象,看看會遇到什麼坑兒。

  1. RegExp(pattern [, flags])

TEST 0:

var url = "https://dx.sansan.com:8080/test/index";
var origin = new RegExp("^https?://[\w-.]+(:\d+)?","i").exec(url)[0];

結果:null, 即沒有找到任何匹配結果

怎麼會這樣??!!沒有問題呀,我從新檢查了每個字符具體匹配過程,確實沒問題呀。。。。

TEST 1:

var url = "https://dx.sansan.com:8080/test/index";
var origin = new RegExp("^https?://[\\w-.]+(:\\d+)?",i).exec(url)[0];

結果:https://dx.sansan.com:8080

竟然匹配成功了!!!這是個什麼鬼?

原來是由於使用new RegExp(pattern [, flags])去建立一個正則對象,pattern 是一個 string 類型,因此 pattern 裏面的全部字符都是要匹配的真實字符。如 pattern = 「\w」,就是從「\world」中匹配「w」這個字母,而不是匹配 0-9或 A-Z 或 a-z 中的一個字符。因此要想達到後一種效果,就得寫成pattern = 「\\w」。這也解釋了匹配"."這個字符時,不用寫成「\.」(由於.是正則中的特殊字符,要想匹配"."字符通常要寫成"\.")。

我實驗後的結論是:使用new RegExp(pattern [, flags]),pattern中字符就是要匹配的字符,其中單一的「\」會被忽略,如"\w"=="w"; 要想使用正則中的包含「\」的特殊符號(如\w和\d),得寫成"\\w"和"\\d"。

  1. /pattern/flags

TEST 0:

var url = "https://dx.sansan.com:8080/test/index";
var origin = /^https?://[\w-.]+(:\d+)?/i.exec(url)[0];

結果:報錯 unexpected SyntexError !!

語法錯誤,這個比較容易排查。發現是"?://"中的"/"與 /pattern/flags 中的"/"衝突了,在這種方式下,"/"字符變成了一個特殊字符。

TEST 1:

var url = "https://dx.sansan.com:8080/test/index";
var origin = /^https?:\/\/[\w-.]+(:\d+)?/i.exec(url)[0];

結果:https://dx.sansan.com:8080

匹配成功了!
因此這種方式匹配時,正則中的特殊符號如\w和\d是生效的,但要想匹配"//",得寫成"\/\/"。

結語

因此之前本身的潛意識認知就存在錯誤。
下面兩種方式匹配字符串時,pattern 除了是不是字符串類型,寫法上有些字符也是不一樣的。

  • RegExp(pattern [, flags]), 如 new RegExp("ab+c", "i");

  • /pattern/flags, 如 /ab+c/i;

相關文章
相關標籤/搜索