踩雷-網址參數編碼、網址解析邏輯
幾天前上班時和同伴討論一個遇到的問題:
他要在一個導向到新增資料的頁面之前插個檢查的步驟,因此要再檢查的步驟指定導向的連結。
原本的api功能根據傳來的資料資料其ID存在與否來判斷是更新還是新增,所以這部分也要寫在「導向的連結」內。
列出來大概像這樣:
檢查的步驟: /chkActiveProcess 導向的頁面: /insertForm?itemId=12345
但是有種情況是使用者要新增一筆資料,其內容複製於現有的某筆資料,因此同伴做了這樣的改動:
檢查的步驟: /chkActiveProcess 導向的頁面: /insertForm?itemId=12345&isNewItem=yes
使用一個新的參數判斷這種情況,然後問題就發生了。
重導向之後顯示的連結,不管怎麼試都看不到isNewItem=yes的部分。
這邊插播補充一下網址解析的小知識,一個網址當中夾帶的資訊包含:
要用什麼連線方式、連到哪台主機的名稱/座標、路徑、以及參數。
舉個例子,
http://example.com/add?a=1&b=2
打好連結按下enter時會由左到右解析出如上述的連線必要資訊
首先, http 是一種連線方式,其他還有https、ftp等姑且不談,後面固定接「://」的文字;
接著 example.com 是連線主機的名稱,從「://」開始到下一個「/」前結束;
路徑 /add 就像是給這網頁的指令,要執行什麼功能,如果路徑是空的視同訪問根目錄,匹配到「?」之前,如果沒有「?」就表示沒有參數;
最後是「?」之後,以「參數名=值」的形式傳遞,多個參數間以「&」分隔,因此列出參數:
a -> 1 b -> 2
回到正題,同伴使用nodejs,部分程式碼如下:
let redirectTo = '/insertForm?itemId=12345&isNewItem=yes' ... endpoint: `/chkActiveProcess?redirectTo=${redirectTo}`
很簡單的字串相加,組合後會像這樣:
/chkActiveProcess?redirectTo=/insertForm?itemId=12345&isNewItem=yes
用上述的定義解釋這串網址實際執行後,參數部分會被如何解讀:
redirectTo -> /insertForm?itemId=12345 isNewItem -> yes
可以看到導向時想帶過去的參數少一個,被當成不相干的另一個參數了
要避免這種事情,需要使用網址傳遞資料時使用的編碼,代表用了這種編碼的部份都是資料,不會被誤判。
詳細可參考:https://zh.wikipedia.org/wiki/百分號編碼
以這次的例子,只要將「&」換成「%26」就行了,程式修正後如下
let redirectTo = '/insertForm?itemId=12345%26isNewItem=yes' ... endpoint: `/chkActiveProcess?redirectTo=${redirectTo}`
不過%26這樣的東西,乍一看是認不出來的,打錯成%25搞不好也認不出來,這時就要善用工具了
各家語言都有網址編碼的函式可用,以javascript來說,可以這樣寫:
let redirectTo = '/insertForm?itemId=12345&isNewItem=yes' ... endpoint: `/chkActiveProcess?redirectTo=${encodeURIComponent(redirectTo)}`
如此一來,不只是&,連同其他特殊符號如/、?、=都會被編碼(雖然這部分不編碼也不會出錯)。
這應該算是很基礎的常識,留個記錄希望以後不要再犯了。