【问题标题】:Url Form Action Without ViewContext没有 ViewContext 的 URL 表单操作
【发布时间】:2010-09-26 16:01:41
【问题描述】:

是否可以在不知道 ViewContext 的情况下从操作中获取 URL(例如,在控制器中)?像这样的:

LinkBuilder.BuildUrlFromExpression(ViewContext context, Expression<Action<T>> action)

...但使用 Controller.RouteData 而不是 ViewContext。我好像有金属块。

【问题讨论】:

    标签: asp.net-mvc url routes


    【解决方案1】:

    这是我在单元测试中的做法:

        private string RouteValueDictionaryToUrl(RouteValueDictionary rvd)
        {
            var context = MvcMockHelpers.FakeHttpContext("~/");
            // _routes is a RouteCollection
            var vpd = _routes.GetVirtualPath(
                new RequestContext(context, _
                    routes.GetRouteData(context)), rvd);
            return vpd.VirtualPath;
        }
    

    每cmets,我会适应一个控制器:

    string path = RouteTable.Routes.GetVirtualPath(
        new RequestContext(HttpContext, 
            RouteTable.Routes.GetRouteData(HttpContext)),
        new RouteValueDictionary( 
            new { controller = "Foo",
                  action = "Bar" })).VirtualPath;
    

    用真实姓名替换“Foo”和“Bar”。这不是我的想法,所以我不能保证它是最有效的解决方案,但它应该能让你走上正确的轨道。

    【讨论】:

    • 我不需要当前请求的 URL,但需要一些其他操作。
    • 我明白了。上面的解决方案仍然适用。那里没有与当前请求的没有连接。
    • 那我不明白。假设我想获取 FooController.Bar("foo", "bar") 的 Url,即“/MyVirtualDir/Foo.mvc/Bar/foo/bar”。我怎么能用它在(不同的)控制器方法中得到它?
    • 克雷格,您是否也可以使用强类型控制器(即 Microsoft.Web.MVC)更新您的答案,而不必对控制器名称和操作进行硬编码?这可能吗?
    • 所有控制器都是强类型的。如果您可以承受性能损失,您当然可以反映控制器和操作名称,但不要忘记 ActionNameAttribute。如果我这样做,我会尝试找到一种方法来使用框架的内部控制器和动作名称缓存。
    【解决方案2】:

    克雷格,感谢您的正确答案。它工作得很好,它也让我思考。因此,为了消除那些抗重构的“魔术字符串”,我开发了一种针对您的解决方案的变体:

    public static string GetUrlFor<T>(this HttpContextBase c, Expression<Func<T, object>> action)
        where T : Controller
    {
        return RouteTable.Routes.GetVirtualPath(
            new RequestContext(c, RouteTable.Routes.GetRouteData(c)), 
            GetRouteValuesFor(action)).VirtualPath;
    }
    
    public static RouteValueDictionary GetRouteValuesFor<T>(Expression<Func<T, object>> action) 
        where T : Controller
    {
        var methodCallExpresion = ((MethodCallExpression) action.Body);
        var controllerTypeName = methodCallExpresion.Object.Type.Name;
        var routeValues = new RouteValueDictionary(new
        {
            controller = controllerTypeName.Remove(controllerTypeName.LastIndexOf("Controller")), 
            action = methodCallExpresion.Method.Name
        });
        var methodParameters = methodCallExpresion.Method.GetParameters();
        for (var i = 0; i < methodParameters.Length; i++)
        {
            var value = Expression.Lambda(methodCallExpresion.Arguments[i]).Compile().DynamicInvoke();
            var name = methodParameters[i].Name;
            routeValues.Add(name, value);
        }
        return routeValues;
    }
    

    我知道有些人会说什么……可怕的反思!在我的特定应用程序中,我认为可维护性的好处超过了性能问题。我欢迎任何关于这个想法和代码的反馈。

    【讨论】:

    • 好的,这是测试控制器的问题(任何使用 Routes 和 RequestContext 的方法)。我不知道如何正确地存根 HttpContext(即使在使用 Reflector 之后)。我将放置一个接口以使控制器更易于测试。
    • 我能够弄清楚如何存根 HttpContext 以使其在测试中发挥作用。缺少的价格是:SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Return("~/");
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    相关资源
    最近更新 更多