前言
問題
專案中使用 golang + gin 開發網站,並且有使用 JSON-LD,然而 JSON-LD 中的「&」字符卻預期外的被替換成了「\x26」。
問題程式碼
發生問題的程式碼如下。
相關問題
經過一番搜尋,發現 Golang「Hugo」框架也有一樣的問題,Hugo 可以透過 {{ printf "\"%s\"" .Title | safeJS }} 解決。有興趣的朋友可以參考下方連結。
追查問題所在
gin 使用 (gin.Context) HTML 方法渲染網頁,而深入追查至最後,可以發現 gin 是使用 html/template 進行渲染。
因此我們經由下方程式碼追查 html/template 後,發現問題不單純是 gin 框架造成,
而是 html/template 原本就有這樣的問題,而 gin 使用了 html/template 進行渲染。
使用 html/template 重現問題
追蹤 Hugo解決方案
深入追查後,發現 Hugo 的「safeJS」解決方案是框架中加入的 template FuncMap,
為此我們撰寫了下列程式測試,並且確實成功打印了「&」符號。
我們可以從程式碼中看到,其實「safeJS」只是 Hugo 框架利用 cast 將輸入值轉為字串,最終輸出為 template.JS,並且上述的解決方案還還有一個步驟,就是將雙引號一起透過 template.JS 轉換後打印,若我們將 cast 拿掉,程式如下
結論
這邊我們得到一個結論:
html/template 在打印時,會被編碼
ex:
"description": "{{.testTextWithAnd}}" ===> "description": "a \x26 b"
ex:
"description": {{.testTextWithAnd}} ===> "description": "a \u0026 b"
解決方案
我們如果將要打印到 script 中的參數,先透過 template function,使用 template.JS (html/template)方法,返回參數後打印,就可以解決被編碼的問題。
若使用 text/template 則不會有此問題
請注意,這邊一定要使用 printf,再將加上雙引號的參數給 safeJS 處理。
否則,若是將參數進行 safeJS 處理,又在參數外加上雙引號渲染,還是會被編碼。