【问题标题】:Avoid hard-coding controller and action names避免硬编码控制器和动作名称
【发布时间】:2011-06-30 09:33:03
【问题描述】:

ASP.NET MVC 似乎鼓励我使用硬编码字符串来引用控制器和操作。

例如,在控制器中:

return RedirectToAction("Index", "Home");

或者,在视图中:

Html.RenderPartial("Index", "Home");

我不想在我的代码中使用硬编码字符串。我该怎么做才能避免这种情况?

【问题讨论】:

  • 这个恕我直言没有问题。在某些时候,您必须告诉代码指向特定的类或函数。
  • @Badger 问题是如果你重命名你的动作/控制器。然后,您必须以某种方式找到所有硬编码的字符串来更新它们,并且您不能依赖编译器来告诉您您遗漏了什么。
  • ReSharper 可以解决这个问题
  • @Dolbz 这就是你编写联合测试的原因:)
  • @frennky 我宁愿不为编译器可以通过零努力为我合理找到的东西编写单元测试;)

标签: c# asp.net-mvc model-view-controller asp.net-mvc-3


【解决方案1】:

在我看来,您想使用强类型重定向。我创建了一个名为 RedirectionHelper 的静态帮助器类,它具有以下方法:

public static string GetUrl<T>(Expression<Action<T>> action, RequestContext requestContext, RouteValueDictionary values = null) where T : Controller
{
    UrlHelper urlHelper = new UrlHelper(requestContext);
    RouteValueDictionary routeValues = ExpressionHelper.GetRouteValuesFromExpression(action);

    if (values != null)
        foreach (var value in values)
            routeValues.Add(value.Key, value.Value);

    return urlHelper.RouteUrl(routeValues);
}

唯一需要注意的是,您必须使用 Nuget 上提供的 Microsoft.Web.Mvc 期货库。

现在,为您的控制器创建一个基本控制器,所有控制器都从该控制器继承该方法:

protected RedirectResult RedirectToAction<T>(Expression<Action<T>> action, RouteValueDictionary values = null) where T : Controller
{
    return new RedirectResult(RedirectionHelper.GetUrl(action, Request.RequestContext, values));
}

现在,在你的行动中,你所要做的就是说:

return RedirectToAction<Controller>(x => x.Index());

同样,您可以编写一个 html 扩展方法,接收相同的参数并构建您的锚标记。

就像您在上面所说的那样,当您更改控制器或动作名称时,您的项目将在编译时中断并显示中断发生的位置。但是,这只会发生在控制器中,因为视图无法编译。

希望这会有所帮助!

【讨论】:

  • 很好 :) 几秒钟后我发布了相同的解决方案
  • 很好 - 比乱用 t4 模板要好得多 - 老实说,这很糟糕
  • 注意:要使此功能起作用,ExpressionHelper 必须是Microsoft.Web.Mvc.Internal.ExpressionHelper不是 System.Web.Mvc.ExpressionHelper
  • 使用这种方式,如何重定向到共享视图?
  • @ArpitKhandelwal 你不能重定向到共享视图。您可以重定向到另一个控制器操作,该操作返回一个共享视图(这是 MVC 原则)。
【解决方案2】:

查看T4MVC,它会生成类,因此您可以拥有强类型的操作和控制器名称。由于它仍然只是到字符串的映射,因此如果您更改控制器名称,重构不会导致视图中的名称更新。

重新生成后,由于名称从生成的类中消失,您会遇到编译错误,因此它在重构和捕获使用硬编码字符串可能遗漏的问题时仍然有用。

【讨论】:

    【解决方案3】:
    【解决方案4】:

    不确定是否有人已经向其中一个 ASP.NET MVC 相关项目添加了扩展方法,但这里有一段代码可用于创建自己的扩展方法:

    public RedirectToRouteResult RedirectToAction<TController>(Expression<Action<TController>> action, RouteValueDictionary routeValues) where TController : Controller
        {
            RouteValueDictionary rv = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);
    
            return RedirectToAction((string)rv["Action"], (string)rv["Controller"], routeValues ?? new RouteValueDictionary());
        }
    
        public ActionResult Index()
        {
            return RedirectToAction<DashboardController>(x => x.Index(), null);
        }
    

    没有参数合并逻辑,所以你必须自己添加它。

    更新:@mccow002 在我之前几秒钟添加了一个类似的解决方案,所以我认为他的解决方案应该被接受。

    【讨论】:

      【解决方案5】:

      我知道这是一个老话题,但是当我在寻找 ASP.NET 5 的答案时,这个主题首次出现。 不再需要硬编码,只需使用 nameof

      [HttpGet]
      public IActionResult List()
      {
         ...
         return View();
      }
      
      [HttpPost]
      public IActionResult Add()
      {
          ...
          return RedirectToAction(nameof(List));
      }
      

      【讨论】:

      • 如果有人通过 ActionName 属性覆盖了动作名称,这将不起作用,例如[ActionName("NewActionName")]
      • @DavidSpence 是的,它也不能与控制器一起工作,你需要实现将切断“控制器”后缀的扩展方法。然而,这就是事情。
      猜你喜欢
      • 2018-10-29
      • 1970-01-01
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      相关资源
      最近更新 更多