ASP.NET MVC中的魔术字符串

时间:2011-12-03 21:16:21

标签: asp.net-mvc refactoring magic-string

我有桌面软件开发的背景,我开始学习ASP.NET MVC。

在我的默认HomeController中,我有一个Index动作,其代码如下所示:

if (!Request.IsAuthenticated)
    return RedirectToAction("Login", "Account");

换句话说,将用户重定向到“/ account / login”。然后,AccountController.Login操作将处理用户并在他成功登录后将其发送回HomeController。

这段代码对我来说很有趣,也许只是因为我习惯于在桌面软件中做不同的事情。如果我将登录操作的名称更改为“LogOn”怎么办?如果我完全删除AccountController并用其他东西替换它会怎么样?我将介绍一个新的bug,但我不会遇到编译器错误,我的单元测试可能也不会捕获它。由于我使用字符串来指定控制器和动作名称,因此重构和重新设计更有可能在整个地方破坏代码。

我想要的是这样的:

if (!Request.IsAuthenticated)
    return RedirectToAction(() => AccountController.Login);

但是我不确定这是否可能,或者这是否是最佳方式。

我是傻瓜,还是其他人有同样的问题?你怎么做才能解决它?

7 个答案:

答案 0 :(得分:20)

我认为您正在寻找的是 T4MVC 存在的原因 - 它删除了与控制器和操作相关的所有“魔术字符串”,并用类和属性替换它们

使用T4MVC,这个

if (!Request.IsAuthenticated)
    return RedirectToAction("Login", "Account");

变成这个

if (!Request.IsAuthenticated)
    return RedirectToAction(MVC.Account.Login());

有一个标志可以在T4MVC设置中设置,以强制它在每个构建上运行模板,在事情发生变化时提供预警。

虽然不是您提出的要求,但您可以考虑使用AuthorizeAttribute来删除是否需要检查请求是否在控制器操作中进行了身份验证。

public class HomeController : Controller
{
    public ActionResult Index() 
    {
        if (!Request.IsAuthenticated)
            return RedirectToAction("Login", "Account"); 

        // .... carry on
    }
}

成为

public class HomeController : Controller
{
    [Authorize]
    public ActionResult Index() 
    {
        // .... carry on
    }
}

然后在web.config中,将网址设置为指向帐户登录网址

<authentication mode="Forms">
   <forms loginUrl="account/login" timeout="30" />
</authentication> 

当然,如果您的控制器和操作发生变化(类似于您的原始投诉),这并不会给您带来任何安全感,但您始终可以设置路径将所选URL指向正确的控制器和操作并使用T4MVC路由中生成的类,如果事情发生了变化,会为您提供一些编译时警告。

答案 1 :(得分:4)

您可以编写自己的自定义扩展方法来帮助您避免魔术字符串。例如,您可以在此处查看我的实现: https://github.com/ivaylokenov/ASP.NET-MVC-Lambda-Expression-Helpers 请记住,这会增加一些性能开销运行时间,您可以通过缓存所有链接来解决这些问题。如果您想要编译时检查T4MVC是您的解决方案:http://t4mvc.codeplex.com/

答案 2 :(得分:4)

在C#6中,你可以利用nameof并轻松地重构许多这些神奇的字符串。

... = new SelectList(context.Set<User>(), nameof(User.UserId), nameof(User.UserName));

...
return RedirectToAction(nameof(Index));

答案 3 :(得分:3)

我知道这不是一个真正的答案,但使用像Resharper这样的工具也可以帮助你解决这个问题。 Resharper会跟踪控制器和操作,并在某些应该是Controller的魔术字符串不是一个时发出警告。它仅适用于RedirectToAction或ActionLink等标准方法。

编辑:显然,您可以添加注释,使其适用于您的自定义扩展方法。请参阅here

答案 4 :(得分:1)

Russ的回答是正确的,但它并没有真正解决你的担忧..这真的是“为什么MVC中有魔术字符串?”。

在早期版本的MVC中,它实际上是完全不同的。他们没有使用魔术字符串,并且有更多基于类型的方法。但是,由于多种原因,这种情况发生了变化。我已经忘记了细节,但是有一些合理的原因可以解释这种非类型安全方法。

我似乎记得它可能与MVC搜索多个区域的匹配约定的方式有关,而且使用字符串比使用类型对象更容易实现。对于视图和局部视图尤其如此。

也许有人会记住这些细节。

答案 5 :(得分:0)

作为T4MVC的替代品,我编写了一个小帮手,它允许您使用几乎完全建议的语法,而无需自动生成代码:

<a href="@(Url.To<MyController>().MyAction("foo", 42))">link</a>

在控制器中:

return Redirect(Url.To<MyController>().MyAction("foo", 42).ToString());

我的解决方案是在运行时执行T4MVC在构建时执行的操作。

NuGet packageGitHub project

答案 6 :(得分:0)

如果您真的只关心重定向到操作和控制器,那么C#6 nameof运算符确实非常有用 - 直到您需要在控制器之间重定向。我最后编写了一个实用程序方法来获取控制器名称:

public class Utilities
{
    public static string ControllerName<T>() where T : Controller
    {
        var name = typeof(T).Name;
        return name.Substring(0, Math.Max(name.LastIndexOf(nameof(Controller), 
                                          StringComparison.CurrentCultureIgnoreCase), 0));
    }
}

你可以像这样使用它:

public ActionResult Foo()
{
    return RedirectToAction(nameof(HomeController.Index), 
                            Utilities.ControllerName<HomeController>());
}