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

ruoyiqing發表於2015-12-30

前言

最近遇到一個需求:用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物件有兩種方法:

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

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

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

匹配 origin 的正則 pattern 如下所示,能匹配http/https協議、域名中帶“-”、“_”字元如“dx-meituan.dxw_mei.com”。至於具體如何匹配到每一個字元的,請自行參考RegExp物件

^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;

相關文章