承接前文 Action Filter 介紹,本文實作一個防盜連結 Filter,攔截非本站域名的圖片/檔案請求,回傳警告圖片或訊息。
public class AntiOutSiteLinkAttribute : ActionFilterAttribute, IActionFilter
{
public AntiOutSiteLinkAttribute(FileType fileType)
{
this.FileType = fileType;
}
public FileType FileType { get; set; }
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase httpContext = filterContext.HttpContext;
if (httpContext.Request.UrlReferrer != null)
{
string serverDomain = httpContext.Request.Url.Host;
string refDomain = httpContext.Request.UrlReferrer.Host;
if (GetRootDomain(refDomain).Equals(GetRootDomain(serverDomain), StringComparison.OrdinalIgnoreCase))
{
return; // 同根域名,放行
}
}
ContentResult cr = new ContentResult();
if (FileType == FileType.Image)
{
cr.ContentType = "image/jpeg";
FileInfo fi = new FileInfo(httpContext.Server.MapPath("~/Content/images/outsitelink.jpg"));
if (fi.Exists)
{
httpContext.Response.WriteFile(fi.FullName);
}
else
{
// 動態產生警告圖片
Bitmap bmp = new Bitmap(200, 50);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.White, 0, 0, 200, 50);
g.DrawString("請不要盜連", new Font("Arial", 15), Brushes.Red, new PointF(0, 0));
bmp.Save(httpContext.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Gif);
}
}
else
{
cr.ContentType = "text/html";
cr.Content = string.Format("請不要盜連。返回<a href='{0}'>{1}</a>", Utils.AbsoluteWebRoot, BlogSettings.Instance.Name);
}
filterContext.Result = cr; // 短路管線,直接回傳結果
}
private string GetRootDomain(string domain)
{
if (string.IsNullOrEmpty(domain))
throw new ArgumentNullException("domain");
string[] arr = domain.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
return (arr.Length <= 2) ? domain : arr[arr.Length - 2] + "." + arr[arr.Length - 1];
}
}
public enum FileType { File = 1, Image }
Request.UrlReferrer.Host,若為空(直接輸入網址)視為盜連。example.com),忽略子域名差異。FileType.Image 回傳圖片;其他回傳 HTML 連結。filterContext.Result,阻止後續 Action 執行。[AntiOutSiteLink(FileType.Image)]
public ActionResult ShowImage(string id) { ... }
Action Filter 讓橫切關注點(如防盜連、權限、快取)以聲明式方式套用,程式碼集中、可重用、易維護。此範例展示完整攔截與回應邏輯,可直接套用於專案中。