9

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

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

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

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

这段代码对我来说很臭,也许只是因为我习惯于在桌面软件中以不同的方式做事。如果我将登录操作的名称更改为“LogOn”会怎样?如果我完全删除 AccountController 并用其他东西替换它会怎样?我将引入一个新错误,但不会出现编译器错误,而且我的单元测试也可能无法捕捉到它。由于我使用字符串来指定控制器和操作名称,因此重构和重新设计更有可能在各处破坏代码。

我想要的是这样的:

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

但是,我不确定这是否可能,或者这是否是最好的方法。

是我傻了,还是其他人有同样的问题?你怎么做才能绕过它?

4

7 回答 7

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,设置 url 指向帐户登录 URL

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

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

于 2011-12-03T21:20:19.637 回答
5

在 C# 6 中,您可以利用nameof并轻松重构许多这些魔术字符串。

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

...
return RedirectToAction(nameof(Index));
于 2016-02-01T22:10:33.530 回答
4

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

于 2015-06-03T07:29:50.750 回答
3

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

编辑:显然您可以添加注释以使其与您的自定义扩展方法一起使用。见这里

于 2011-12-03T21:37:11.060 回答
1

Russ 的回答是正确的,但它并没有真正解决您的问题......这实际上是“为什么 MVC 中有魔术字符串?”。

在 MVC 的早期版本中,它实际上是完全不同的。他们没有使用魔术字符串,而是有更多基于类型的方法。但是,由于多种原因,这种情况发生了变化。我忘记了细节,但是这种奇怪的回归到非类型安全方法是有逻辑的原因的。

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

也许记得细节的人可以插话。

于 2011-12-04T07:19:20.990 回答
0

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

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

在控制器中:

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

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

有一个NuGet 包和一个GitHub 项目

于 2014-11-03T16:13:56.580 回答
0

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

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>());
}
于 2016-09-19T00:42:50.443 回答