專案採用 UTF-8 (`web.config globalization requestEncoding="utf-8"`),前端頁面包含連結:
<a href="user.aspx?id=水天">用戶</a>
三種情境結果不同:
- IE 位址列直接輸入 → ✅ 正常
- 靜態 HTML 頁面點擊 → ✅ 正常
- 動態功能區 (經過 JS/Server 處理) 點擊 → ❌ 接收端收到亂碼
**原因**:動態功能區對 URL 執行了 `Server.UrlEncode` (預設 UTF-8),導致中文參數被雙重編碼或編碼不匹配。
核心原理:瀏覽器與伺服器的編碼協商
環節 預設編碼 說明
ASP.NET `Server.UrlEncode` / `HttpUtility.UrlEncode` UTF-8 .NET 預設
傳統 ASP `Server.URLEncode` 系統代碼頁 (GB2312/Big5) 舊系統相容
瀏覽器 Address Bar 輸入 UTF-8 (現代瀏覽器) 依頁面 charset 或系統設定
HTML `` 點擊 頁面 `` 指定編碼 UTF-8 頁面送 UTF-8
JavaScript `escape()` 非標準 (已棄用) 中文編為 `%uXXXX`
JavaScript `encodeURIComponent()` UTF-8 現代標準
解決方案比較
方案一:全域設定 GB2312 (❌ 不推薦)
<globalization requestEncoding="gb2312" responseEncoding="gb2312" />
副作用:整站請求/回應/檔案讀寫全變 GB2312,資料庫、API、前端頁面全線風險。
方案二:單頁面指定 charset=gb2312 (⚠️ 侷限性大)
<meta charset="gb2312" />
動態功能區可能被嵌入各種編碼頁面,不可控。
方案三:雙端協商編碼 (✅ 推薦:統一指定 GB2312 或 UTF-8)
**原則**:發送端 `UrlEncode(指定編碼)`,接收端 `UrlDecode(相同編碼)`。
若無法控制接收端 (如第三方網站),**發送端編碼為接收端預期編碼** (通常 GB2312 相容舊系統)。
方案四:發送端統一轉 GB2312 編碼 (本文核心方案,相容性最佳)
public static class UrlChineseHelper
{
// 協商編碼:GB2312 (相容 ASP/舊系統) 或 UTF-8 (現代標準)
private static readonly Encoding _targetEncoding = Encoding.GetEncoding("GB2312");
///
/// 解析 URL 參數,將所有參數值重新編碼為目標編碼,重組 URL
///
public static string NormalizeChineseUrl(string rawUrl)
{
if (string.IsNullOrEmpty(rawUrl)) return rawUrl;
Uri uri;
try { uri = new Uri(rawUrl); }
catch { return rawUrl; } // 非標準 URL 直接返回
// 解析 QueryString (注意:uri.Query 已被 Uri 類解析,可能已解碼)
// 更穩健:手動解析 OriginalString 中 ? 後部分
int qIndex = rawUrl.IndexOf('?');
if (qIndex < 0) return rawUrl; // 無參數
string baseUrl = rawUrl.Substring(0, qIndex);
string queryPart = rawUrl.Substring(qIndex + 1);
// 解析鍵值對
var pairs = queryPart.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
var newPairs = new System.Collections.Generic.List();
foreach (var pair in pairs)
{
int eq = pair.IndexOf('=');
if (eq < 0) { newPairs.Add(pair); continue; }
string key = pair.Substring(0, eq);
string value = pair.Substring(eq + 1);
// 關鍵:將值用目標編碼 UrlEncode
// 注意:value 可能已是編碼過的字串,需先 UrlDecode (UTF-8) 再 UrlEncode (GB2312)
// 但無法確定原始編碼,實務上假設來源為 UTF-8 (ASP.NET 預設)
string decoded;
try { decoded = HttpUtility.UrlDecode(value, Encoding.UTF8); }
catch { decoded = value; } // 解碼失敗視為原文
string encoded = HttpUtility.UrlEncode(decoded, _targetEncoding);
newPairs.Add(key + "=" + encoded);
}
return baseUrl + "?" + string.Join("&", newPairs);
}
///
/// 簡易版:僅對單一參數值編碼
///
public static string EncodeParam(string value) =>
HttpUtility.UrlEncode(value, _targetEncoding);
}
使用範例
後端產生連結時
string rawUrl = "https://partner.com/user.aspx?id=水天&type=VIP";
string safeUrl = UrlChineseHelper.NormalizeChineseUrl(rawUrl);
// 輸出: https://partner.com/user.aspx?id=%CB%CF%CC%EC&type=VIP (GB2312 編碼)
litLink.Text = $"前往合作夥伴";
前端 JavaScript 動態產生連結
// JS 版 GB2312 編碼需引入編碼庫 (如 iconv-lite / encoding.js)
// 簡易替代:後端提供 API 供前端調用
function buildSafeUrl(baseUrl, params) {
// params = { id: "水天", type: "VIP" }
return fetch('/api/encode-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl, params })
}).then(r => r.json()).then(d => d.safeUrl);
}
// 後端 API
[HttpPost]
public IActionResult EncodeUrl([FromBody] EncodeUrlRequest req)
{
var builder = new UriBuilder(req.BaseUrl);
var query = HttpUtility.ParseQueryString(builder.Query);
foreach (var kvp in req.Params)
query[kvp.Key] = kvp.Value; // 內部會自動 UrlEncode (UTF-8)
// 若需強制 GB2312,需手動重組
string safeUrl = UrlChineseHelper.NormalizeChineseUrl(builder.ToString());
return Json(new { safeUrl });
}
接收端解碼 (若我們是接收方)
// ASP.NET 接收 GB2312 編碼參數
public string GetChineseParam(HttpRequest request, string key)
{
string raw = request.QueryString[key]; // 已自動 UrlDecode (UTF-8) -> 亂碼字串
if (string.IsNullOrEmpty(raw)) return null;
// 還原位元組 -> GB2312 解碼
byte[] bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(raw);
return Encoding.GetEncoding("GB2312").GetString(bytes);
}
// 或 .NET 4.7.2+ 支援直接指定編碼解碼
// return HttpUtility.UrlDecode(raw, Encoding.GetEncoding("GB2312"));
現代化最佳實踐:避免 URL 傳中文,改用 POST / Body / Token
場景 推薦做法
內部頁面跳轉 使用 `Server.UrlEncode` (UTF-8) + 確保接收頁 `requestEncoding="utf-8"`
對接舊系統 (ASP/PHP/Java) 協商編碼 (GB2312/UTF-8),雙端顯式指定,提供正規化輔助類
對接現代 API (REST/GraphQL) **不要把中文放 QueryString**,改用 POST JSON Body / Header / JWT Claims
SEO 友善連結 URL Slug 化:`/user/水天` → `/user/shui-tian` (拼音/英文) 或 `/user/{id}`
前端路由 Vue Router / React Router 使用 `encodeURIComponent` (UTF-8) 統一
總結
URL 中文參數亂碼本質是 **發送端編碼 ≠ 接收端解碼**。解決核心在於 **雙端協商統一編碼** 並在代碼中 **顯式指定** (`HttpUtility.UrlEncode/Decode(value, Encoding.GetEncoding("GB2312"))`)。本文提供的 `UrlChineseHelper.NormalizeChineseUrl` 可直接納入專案共用庫,處理動態產生連結的編碼正規化。長遠而言,**避免在 URL 傳遞中文**,改用 Body/Token/Slug,才是架構層面的根治之道。