URL 驗證應分為 格式檢查(客戶端/伺服器端同步)與 存活檢查(伺服器端發送 HTTP 請求)兩層,兼顧使用者體驗與資料品質。
function isValidUrlFormat(str) {
// 支援 http/https、域名/IP、選擇性連接埠、路徑、查詢字串
const pattern = /^https?:\/\/(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3})(:\d+)?(\/[^\s]*)?$/i;
return pattern.test(str.trim());
}
using System.Text.RegularExpressions;
public static bool IsValidUrlFormat(string url)
{
if (string.IsNullOrWhiteSpace(url)) return false;
// 與 JS 版同邏輯,確保前後端一致
const string pattern = @"^https?://(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){3}\d{1,3})(:\d+)?(/[^\s]*)?$";
return Regex.IsMatch(url.Trim(), pattern, RegexOptions.IgnoreCase);
}
僅檢查格式無法確認網址真正可存取。伺服器端可發送 HEAD 請求(不下載內文),依狀態碼判斷。
using System.Net;
public static bool IsUrlReachable(string url, int timeoutMs = 10000)
{
if (!IsValidUrlFormat(url)) return false;
// 自動補齊 scheme
if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) &&
!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
url = "https://" + url;
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "HEAD"; // 僅取得標頭
request.Timeout = timeoutMs; // 10 秒逾時
request.AllowAutoRedirect = true; // 允許重導向
request.UserAgent = "Mozilla/5.0 (compatible; UrlValidator/1.0)";
using (var response = (HttpWebResponse)request.GetResponse())
{
// 2xx、3xx 視為成功(3xx 代表重導向但最終可達)
return (int)response.StatusCode < 400;
}
}
catch (WebException ex) when (ex.Response is HttpWebResponse resp)
{
// 4xx/5xx 也能拿到回應,視業務需求決定是否視為有效
return (int)resp.StatusCode < 400;
}
catch
{
// DNS 失敗、連線逾時、SSL 錯誤等
return false;
}
}
public JsonResult ValidateUrl(string inputUrl)
{
if (!IsValidUrlFormat(inputUrl))
return Json(new { valid = false, reason = "格式錯誤" });
bool reachable = IsUrlReachable(inputUrl);
return Json(new { valid = reachable, reason = reachable ? "OK" : "無法連線或回應異常" });
}
| 議題 | 建議做法 |
|---|---|
| 大量並發驗證 | 使用 HttpClient + SemaphoreSlim 限制並發數;或放入背景工作佇列非同步處理。 |
| SSRF 風險 | 禁止驗證內網 IP(10.x、172.16-31、192.168、127.0.0.1)、localhost、metadata 端點;僅允許公網域名。 |
| 重導向循環 | 設定 request.MaxAutomaticRedirections = 5。 |
| 憑證錯誤 | 若需忽略 SSL 錯誤(僅測試環境),可實作 ServerCertificateCustomValidationCallback(需 HttpClient)。 |
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10),
DefaultRequestHeaders = { UserAgent = { { "UrlValidator", "1.0" } } }
};
public static async Task IsUrlReachableAsync(string url)
{
if (!IsValidUrlFormat(url)) return false;
if (!url.StartsWith("http")) url = "https://" + url;
try
{
using var request = new HttpRequestMessage(HttpMethod.Head, url);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return response.IsSuccessStatusCode || (int)response.StatusCode < 400;
}
catch { return false; }
}