TIPS-用URL傳送Base64編碼
2 |
ASP.NET網站專案,使用Query String傳送Base64編碼後的參數值(例如: MyApp.aspx?d=RGFya3RocmVhZCBSb2NrcyEh),卻發現測試有時成功有時失敗,最後查出是程式產生URL時沒有對Base64字串進行UrlEncode編碼所造成的問題。
Base64編碼使用了 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",再加上補齊長度的1個或2個"="符號,共計65個字元。其中只有"="與"+"在Query String有特殊含意,"="用來連接參數名稱與值、"+"則用來表示" "(空白符號)。”?a=ab=&b=123”的寫法仍在程式Request.QueryString的識別容忍範圍,可靠"&"符號或結尾認出a = "ab=",但若參數值出現"+",例如: ?a=X+Y,則Request.QueryString["a"]的結果就會變成"X Y"。換句話說,若是Base64的編碼結果好死不死出現"+"(機率不低),在伺服器端取QueryString變數時就會被解讀成" "(空白),導致Base64解碼錯誤。
要解決這個問題,只需使用Server.UrlEncode()或HttpUtility.UrlEncode()對Base64編碼後的字串再做一次編碼即可,如此"+"會變成%2b、"="會被轉成%3d,在Request.QueryString取值時就可正確無誤地還原。
除此之外,若要交由Javascript端處理,則可使用encodeURIComponent()進行編碼(注意: escape()、encodeURI()、encodeURIComponent()的編碼原則不盡相同,這裡有一篇好文章說明三者的差異);再不然,由於其中關鍵只在於"+",而空白符好並非Base64的合法字元,若傳入參數無法加工,可在Server端將讀取結果中的" "再換回"+",雖然嚴謹度較差,但也是種解法。
以下是重現問題及展示解法的範例:
<%@ Page Language="C#" %>
<!DOCTYPE html>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
Encoding enc = Encoding.UTF8;
base64_sample.Value =
Convert.ToBase64String(enc.GetBytes("<A>"));
//故意產生含"+"符號的Base64編碼
string mode = Request["mode"];
string b64 = Request["b64"];
if (!string.IsNullOrEmpty(mode))
{
Response.ContentType = "text/plain";
if (mode == "decode")
{
try
{
string res = enc.GetString(Convert.FromBase64String(b64));
Response.Write("Result=" + res);
}
catch (Exception ex)
{
Response.Write("Error: " + ex.Message);
}
}
else if (mode == "decode-fix")
{
string res = enc.GetString(
Convert.FromBase64String(FixBase64FromQueryString(b64)));
Response.Write("Result(Fixed)=" + res);
}
Response.End();
}
}
//由於QueryString中Base64的"+"會被解讀成空白符號,故用函數還原
string FixBase64FromQueryString(string b)
{
return b.Replace(" ", "+");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Base64 Test</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.2.js"
type="text/javascript"> </script>
<script>
$(function () {
$("input").click(function () {
var v = $("#base64_sample").val();
var m = "decode";
//b2鈕,使用encodeURIComponent對Base64編碼內容再做編碼
//注意: escape()或encodeURI()不會轉換"+",不要用錯
if (this.id == "b2") v = encodeURIComponent(v);
//b3鈕,參數維持原貌(含"+"),在Server端再做處理
if (this.id == "b3") m += "-fix";
var url = location.href + "?mode=" + m + "&b64=" + v;
$("#dUrl").text(url);
$("#fDisp").attr("src", url);
});
});
</script>
<style>
body { padding: 10px; }
div { margin: 5px; }
input { width: 120px; margin-right: 10px; }
</style>
</head>
<body>
<form id="form1" runat="server">
<input type="hidden" id="base64_sample" runat="server" />
<div>
<input type="button" id="b1" value="直接傳送" />
<input type="button" id="b2" value="參數編碼後傳送" />
<input type="button" id="b3" value="Server端修正" />
</div>
<div id="dUrl"> </div>
<iframe id="fDisp" style="width: 400px;"></iframe>
</form>
</body>
</html>
Test 1 沒進行UrlEncode或encodeURIComponent()就直接當成參數傳送會導致錯誤
Test 2 便用encodeURIComponent()轉換,"+"會被轉成%2B可避開問題
Test 3 ASP.NET Server端接入Request[“b64”]後再將空白換回"+"
Comments
# by 讚讚讚
讚讚讚
# by Dean Tsai
先前有看過這篇文章,以為我寫的程式完美可用了,結果不知為何"偶而"會出現錯誤 小弟正愁眉不展,回頭再細看才發現您說jQuery遇到加號會出事!! 黑暗大大認真踩坑的精神令人肅然起敬 感恩大大,讚嘆大大