[一步一步MVC]第一回:使用ActionSelector控制Action的選擇

作者: Anytao  來源: 博客園  發布時間: 2009-05-11 11:22  閱讀: 3956 次  推薦: 0   原文鏈接   [收藏]  
摘要:本文著重于應用,而沒有特別對什么是ActionFilter進行探討,我們在合適的時間再次與MVC握手,對此進行進一步進行討論。

系列文章導航:

[一步一步MVC]第一回:使用ActionSelector控制Action的選擇

[一步一步MVC]第二回:還是ActionFilter,實現對業務邏輯的統一Authorize處理

[一步一步MVC]第三回:MVC范例大觀園

[一步一步MVC]第四回:漫談ActionLink,有時“胡攪蠻纏”

[一步一步MVC]第五回:讓TagBuilder豐富你的HtmlHelper

[一步一步MVC]第六回:什么是MVC(上)?

 

ActionFilter一定是MVC控制中對于Action控制中最值得研究的玩意,在項目實際中我們不可避免的使用例如:

  • HandleError
  • Authorized
  • OutputCache

在本文中,我們應用Action Selector方式進行Action的選擇,想要闡述清楚這個問題,我們從實際的問題出發來關注。

實際的問題,從和老趙的對話了解

我們有個業務室這樣的:系統有不同的角色,例如Admin、Client、Agent。假設有個功能叫Book/List,那么就對應了一個List這樣的View和action為List這樣的BookController,現在,我們的情況是對于不同的角色,所對應的List是不同的。Admin看到的Book/List和Client看到的Book/List是不同的,那么通過Url:http://anytao.com/Book/List/123,不同的角色如何處理,差不多就這樣,是否清楚。

:那么對于同一Action如何更好的return到不同的view?

老趙:具體問題是什么呢?

: 我現在能想到的是在Action中根據角色Return到不同的View,簡單的辦法就是在List Action根據角色Return到不同的View。問題是,還有什么更好的辦法。

老趙:準備n各action,分別加上自定的ActionSelector,不要用一個Action,不用一個Action然后在里面if。

[OnlyInRole("admin")] 
[ActionName("List")]
ListForAdmin() 
{
}

[OnlyInRole("user")] 
[ActionName("List")]
ListForUser() 
{
}

OnlyInRole需要自己寫,不過就幾行話

: 哈哈,差不多了,謝啦。

根據老趙的指導,我對此思路進行了必要的探討,感受果然不同凡響,很好很暴力。

解決方案

為了實現對于Action進行Selector的具體實現,我選擇對ActionNameSelectorAttribute 進行擴展,參考ActionName的實現方式,對于按照RoleType進行過濾的需求顯然有很好的借鑒價值,以ActionNameAttribute為例,其具體實現為:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ActionNameAttribute : ActionNameSelectorAttribute {

    public ActionNameAttribute(string name) {
        if (String.IsNullOrEmpty(name)) {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
        }

        Name = name;
    }

    public string Name {
        get;
        private set;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, 
MethodInfo methodInfo) {
        return String.Equals(actionName, Name, StringComparison.OrdinalIgnoreCase);
    }

}

再來了解ActionNameSelectorAttribute抽象類的定義,其主要提供了對Action進行Select時的IsValidName判斷約定,例如ActionNameSelectorAttribute的定義:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public abstract class ActionNameSelectorAttribute : Attribute {
    public abstract bool IsValidName(ControllerContext controllerContext, string actionName, 
MethodInfo methodInfo);
}

所以,對ActionNameSelecterAttribute進行擴展變得異常簡單,下面是一個最簡單的實現,肯定讓我們耳目一新:

// Release : code01, 2009/04/17                    
// Author  : Anytao, http://www.anytao.com
public class ActionInRoleAttribute : ActionNameSelectorAttribute
{
    public ActionInRoleAttribute(RoleType role)
    {
        this.role = role;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
    {
        if (controllerContext.HttpContext.User.IsInRole(role.ToString()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private RoleType role;
}

我們的邏輯其實很簡單,借助于IPrincipal的IsInRole方法,可以很簡單的根據role的定義進行IsValidName過濾,例如我們的應該可以就是:

// Release : code02, 2009/04/17                    
// Author  : Anytao, http://www.anytao.com
[AcceptVerbs(HttpVerbs.Get)]
[ActionName("List")]
[ActionInRole(RoleType.Client)]
public ActionResult ClientList(int id)
{
    return View(
        "ClientBookList",
        new Book
        {
            ID = id,
            Name = string.Empty
        });
}

// Release : code03, 2009/04/17                    
// Author  : Anytao, http://www.anytao.com
[AcceptVerbs(HttpVerbs.Get)]
[ActionName("List")]
[ActionInRole(RoleType.Admin)]
public ActionResult AdminList(int id)
{
    return View(
        "AdminBookList",
        new Book
        {
            ID = id,
            Name = string.Empty
        });
}

調用List Action時,根據登陸用戶的角色來決定具體執行的Action(ClientList或者AdminList),并由不同的Action導航到不同的View(ClientBookList或者AdminBookList),而對于不同Action訪問的URL都是一樣的(http://anytao.com/Book/List/123),同時避免了在服務層對角色的判斷,某種程度上按照RoleType對于Controller層進行了“注入”,使得Controller層的邏輯不在關心Action過濾的問題。

不過,在應用上還有一些值得注意的問題:

  • 使用非泛型ActionLink方法調用應用ActionInRole 的Action

一般而言,我們提倡應用強類型ModelData在View層進行操作,那么泛型方法ActionLink值得推薦,

<p>
    <%= Html.ActionLink<BookController>(c => c.List(Model.ID), "List") %>
</p>

然而,應用被ActionInRole標記的,同時被ActionName重命名的Action,將不被識別,我們只好以非泛型方式實現對于一名多用的Action來調用:

<p>
    <%= Html.ActionLink("List", "List", new { id=Model.ID }) %>
</p>

  • 在return View中通過制定ViewName進行返回,來選擇適合的View,例如
return View(
    "AdminBookList",
    new Book
    {
        ID = id,
        Name = string.Empty
    });

因為默認情況下,MVC引擎是以ActionName進行返回的,在我們的應用中必須以這種方式進行。

結論

本文著重于應用,而沒有特別對什么是ActionFilter進行探討,我們在合適的時間再次與MVC握手,對此進行進一步進行討論。

0
0
 
標簽:MVC ASP.NET
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()