MVC控制器中的单一责任原则。批评要求

时间:2012-12-05 00:11:54

标签: asp.net-mvc asp.net-mvc-3 design-patterns attributes single-responsibility-principle

在我的MVC4应用程序中,根据您是否已登录(在我的情况下为FormsAuthentication),有些操作需要采取不同的行为。

例如,我有一个AccountController,它有一个方法“RenderAccountAndProfile”。如果注销,相应的局部视图将显示登录提示和按钮。如果用户已登录,则会显示用户的配置文件链接以及注销按钮。

我迄今为止在项目中采用的方法是简单地使用if语句......

        if (HttpContext.User.Identity.IsAuthenticated)
        {
            ...
        }
        else
        {
            ...
        }

然而,我刚刚创造了我认为这种方法的替代方案。

我创建了一个名为AnonymousUsersOnly的新属性,非常简单:

public class AnonymousUsersOnlyAttribute : System.Web.Mvc.ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(System.Web.Mvc.ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return !controllerContext.HttpContext.User.Identity.IsAuthenticated;
    }
}

我的AccountController类使用Authorize属性进行修饰。这使我能够拥有以下代码:

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    [AnonymousUsersOnly]
    [ActionName("RenderAccountAndProfile")]
    public ActionResult RenderAccountAndProfile_Anonymous()
    {
        // create a "logged out" view model
        return Content("**NOT LOGGED IN** - LOG IN HERE");
    }

    [ActionName("RenderAccountAndProfile")]
    public ActionResult RenderAccountAndProfile_Authorized()
    {
        // create a "logged in" view model
        return Content("**LOGGED IN** - LOG OUT");
    }
}

我非常喜欢这种方法,因为我的动作方法符合Single Responsibility Principle。现在,每种方法只处理登录情况或登出情况。我不再需要任何“if”语句来指导流量。

这也应该使单元测试更容易,因为每种方法现在只关注一个结果,而不是两个。我们可以编写单元测试来分别测试每个结果,调用不同的方法。

显然,我不能有两个具有相同签名的方法,这就是我必须使用ActionName属性的原因。

我很感激你的批评。你认为这是一个优雅的解决方案吗?这种方法的优缺点是什么?这有什么安全隐患/风险?

2 个答案:

答案 0 :(得分:3)

您遇到的问题是策略模式问题。而且你已经实现了一个非标准的(非标准)策略模式。我担心它太聪明了。这种聪明才能使代码对于那些没有经验的人来说显得不那么明显。

副手,我不喜欢不打扰。我经常将控制器编写为域对象/服务上的非常瘦的适配器。因此,我愿意采取务实的方法来设计控制器的完美性。在决定轻微的设计问题和明显的代码之间,总是选择明显的代码。

如果你有更厚的控制器,或者其他一些在这里真正关心这个问题的原因,你可能会考虑一种更传统的策略模式,也许是一个抽象工厂,它提供了基于身份验证状态的不同策略实现。这符合您的设计目标,并且对其他程序员更熟悉(如果他们了解设计模式)。

所有这一切,我不认为保持聪明的解决方案会伤害到那么多。我很想改名字;拒绝对我来说似乎是一个奇怪的动词。或许AnonymousUsersOnly,这对未来的程序员来说会更具沟通性。

答案 1 :(得分:0)

我最好选择(伪代码):

PartialView UserProfile() { ... }

PartialView Login() { ... }

并在视野中:

if (User.IsAuthenticated) {
    @Html.Action("UserProfile")
} else {
    @Html.Action("Login")
}

也可以是DisplayTemplate,帮助器或任何你喜欢的东西,所以你最终使用

@Html.DisplayFor(m=> User)