首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET MVC ActionMethodSelector 實作多角色對應同一 URL 不同 View

     編輯:關於ASP.NET

MVC 路由將 URL 對應至 Controller/Action,但當「同一 URL 需根據用戶角色回傳不同 View」時,傳統做法在 Action 內部 if/else 判斷角色並 return View("ViewName"),違反開放封閉原則。本文介紹 ActionMethodSelectorAttribute 讓路由引擎根據自訂條件自動選擇正確 Action 方法。

情境:Book/List 依角色顯示不同內容

  • Admin:顯示所有書籍、管理按鈕
  • Client:僅顯示可購買書籍
  • Agent:顯示代銷書籍與佣金資訊

URL 皆為 /Book/List,不想在單一 Action 中寫大量分支邏輯。

核心機制:ActionMethodSelectorAttribute

ActionMethodSelectorAttribute 抽象類別提供 IsValidForRequest(ControllerContext, MethodInfo) 方法,路由引擎在動作選擇階段呼叫,回傳 true 表示該 Action 符合目前請求。

實作 OnlyInRoleAttribute

using System;
using System.Reflection;
using System.Web.Mvc;

namespace MyProject.ActionSelectors
{
    [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class OnlyInRoleAttribute : ActionMethodSelectorAttribute
    {
        private readonly string _role;

        public OnlyInRoleAttribute(string role)
        {
            _role = role ?? throw new ArgumentNullException(nameof(role));
        }

        public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
        {
            if (controllerContext == null) throw new ArgumentNullException(nameof(controllerContext));
            return controllerContext.HttpContext.User.IsInRole(_role);
        }
    }
}

Controller 定義多個同名 Action

搭配 [ActionName("List")] 將不同方法對應至相同 Action 名稱:

public class BookController : Controller
{
    [OnlyInRole("Admin")]
    [ActionName("List")]
    public ActionResult ListForAdmin()
    {
        var books = _bookService.GetAllBooks();
        return View("List_Admin", books);
    }

    [OnlyInRole("Client")]
    [ActionName("List")]
    public ActionResult ListForClient()
    {
        var books = _bookService.GetAvailableBooks();
        return View("List_Client", books);
    }

    [OnlyInRole("Agent")]
    [ActionName("List")]
    public ActionResult ListForAgent()
    {
        var books = _bookService.GetAgentBooks(User.Identity.Name);
        return View("List_Agent", books);
    }

    // 預設 fallback(無角色或角色不符)
    [ActionName("List")]
    public ActionResult ListDefault()
    {
        return View("List_Guest", _bookService.GetFeaturedBooks());
    }
}

運作流程

  1. 請求進入 /Book/List。
  2. 路由引擎找到 BookController 所有 [ActionName("List")] 方法。
  3. 依序呼叫各方法的 OnlyInRoleAttribute.IsValidForRequest。
  4. 第一個回傳 true 的方法被選中執行。
  5. 若皆為 false,拋出 HttpException 404(或落入無屬性的 fallback 方法)。

優點與注意事項

優點注意事項
Action 專注單一角色邏輯,符合 SRP角色檢查發生在 Authorization 階段之後,確保 User 已驗證
無需在 Action 內寫 if/else,易測試方法數隨角色增加,建議搭配共用私有方法減少重複
URL 保持乾淨,無需查詢字串區分順序敏感:先宣告的優先匹配,fallback 放最後
支援單元測試模擬 ControllerContext複雜條件建議改用 Policy-based Authorization (ASP.NET Core)

擴展:複合條件選擇器

可建立 WhenAttribute 接受 Func<ControllerContext, bool>,支援任意條件:

public class WhenAttribute : ActionMethodSelectorAttribute
{
    private readonly Func<ControllerContext, bool> _predicate;
    public WhenAttribute(Func<ControllerContext, bool> predicate) { _predicate = predicate; }
    public override bool IsValidForRequest(ControllerContext cc, MethodInfo mi) => _predicate(cc);
}

// 使用範例
[When(c => c.HttpContext.Request.IsAjaxRequest())]
[ActionName("List")]
public ActionResult ListAjax() { return PartialView("_ListPartial"); }

結語

ActionMethodSelectorAttribute 是 MVC 管線中鮮為人知但極具威力的擴展點,能將「請求條件判斷」從 Action 移至框架層,實現真正的關注點分離。雖 ASP.NET Core 推薦使用 IActionConstraint 與 Policy-based Authorization,但此模式在 MVC 5 維護專案中依然實用且優雅。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved