【问题标题】:url.Action with MvcContrib generates invalid links带有 MvcContrib 的 url.Action 生成无效链接
【发布时间】:2013-01-08 11:08:05
【问题描述】:

在我们的应用程序中,我们使用 MvcContrib 生成链接,但跨区域链接除外,其中 Contrib 似乎无法正常工作(或者我们做错了什么)。在服务中,我们有一个生成 List 的函数,其中包含 url 和其他用于通过自定义 html 助手生成 tabstrib 的属性。该函数将数据库对象的 id 和 UrlHelper 作为参数,以帮助创建链接。

m_service.GenerowanieZakladkiDlaKontrolera_ARCH_Akt(idAktu, new UrlHelper(this.ControllerContext.RequestContext));

然后在 GenerowanieZakladkiDlaKontrolera_ARCH_Akt 我们有这样的东西:

model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Akt", Url = "" });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Wzmianki", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_WzmiankiController>(c => c.Index(idAktu)) });
if (tekstJednolity.StanTekstuJednolitego == "RB" || tekstJednolity.StanTekstuJednolitego == "SW")
{
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "t.j. aktu", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_TekstJednolityController>(c => c.Edytuj(tekstJednolity.Id)) });
}
else
{
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "t.j. aktu", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_TekstJednolityController>(c => c.Raport(tekstJednolity.Id)) });
}
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 1", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek1Controller>(c => c.Index(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 2", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek2Controller>(c => c.Index(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 3", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek3Controller>(c => c.Edytuj(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 4", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek4Controller>(c => c.Edytuj(idAktu)) });

现在的问题是,在某些同事计算机上,它会正确生成指向操作的链接,而在某些计算机上,它看起来像是从我们的应用程序中获取随机区域并尝试创建无效链接。我们可以使用一个简单的 url.Action("action","controler") ,它在所有方面都可以正常工作,但我们更喜欢 MvcContrib :)。有谁知道为什么会这样?或者可以分享一个替代方案吗?

【问题讨论】:

    标签: asp.net-mvc-3 mvccontrib urlhelper


    【解决方案1】:

    似乎在下面使用的 LinkBuilder 根本没有使用 GetVirtualPatchForArea,正如我所读的那样,这是 MVC 错误。所以我决定制作自己的 HtmlHelper 使用该方法:

     public static string ActionArea<TController>(this HtmlHelper urlHelper, Expression<Action<TController>> expression) where TController : Controller
    {
         RouteValueDictionary routeValues = GetRouteValuesFromExpression(expression);
         VirtualPathData vpd = new UrlHelper(urlHelper.ViewContext.RequestContext).RouteCollection.GetVirtualPathForArea(urlHelper.ViewContext.RequestContext, routeValues);
       return (vpd == null) ? null : vpd.VirtualPath;
    }
    
    public static string ActionArea<TController>(this UrlHelper urlHelper, Expression<Action<TController>> expression) where TController : Controller
            {
                RouteValueDictionary routeValues = GetRouteValuesFromExpression(expression);
                VirtualPathData vpd = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeValues);
                return (vpd == null) ? null : vpd.VirtualPath;
            }
    
    public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller
            {
                if (action == null)
                {
                    throw new ArgumentNullException("action");
                }
    
                MethodCallExpression call = action.Body as MethodCallExpression;
                if (call == null)
                {
                    throw new ArgumentException("Akcja nie może być pusta.", "action");
                }
    
                string controllerName = typeof(TController).Name;
                if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
                {
                    throw new ArgumentException("Docelowa klasa nie jest kontrolerem.(Nie kończy się na 'Controller')", "action");
                }
                controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);
                if (controllerName.Length == 0)
                {
                    throw new ArgumentException("Nie można przejść do kontrolera.", "action");
                }
    
                // TODO: How do we know that this method is even web callable?
                //      For now, we just let the call itself throw an exception.
    
                string actionName = GetTargetActionName(call.Method);
    
                var rvd = new RouteValueDictionary();
                rvd.Add("Controller", controllerName);
                rvd.Add("Action", actionName);
    
                var namespaceNazwa = typeof(TController).Namespace;
    
                if(namespaceNazwa.Contains("Areas."))
                {
                    int index = namespaceNazwa.IndexOf('.',namespaceNazwa.IndexOf("Areas."));
                    string nazwaArea = namespaceNazwa.Substring(namespaceNazwa.IndexOf("Areas.") + 6, index - namespaceNazwa.IndexOf("Areas.") + 1);
                    if (!String.IsNullOrEmpty(nazwaArea))
                    {
                        rvd.Add("Area", nazwaArea);
                    }
                }
    
                //var typ = typeof(TController).GetCustomAttributes(typeof(ActionLinkAreaAttribute), true /* inherit */).FirstOrDefault();
                /*ActionLinkAreaAttribute areaAttr = typ as ActionLinkAreaAttribute;
                if (areaAttr != null)
                {
                    string areaName = areaAttr.Area;
                    rvd.Add("Area", areaName);
                }*/
    
                AddParameterValuesFromExpressionToDictionary(rvd, call);
                return rvd;
            }
    
    
    private static string GetTargetActionName(MethodInfo methodInfo)
            {
                string methodName = methodInfo.Name;
    
                // do we know this not to be an action?
                if (methodInfo.IsDefined(typeof(NonActionAttribute), true /* inherit */))
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
                        "Nie można wywoływać metod innych niż akcje.", methodName));
                }
    
                // has this been renamed?
                ActionNameAttribute nameAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true /* inherit */).OfType<ActionNameAttribute>().FirstOrDefault();
                if (nameAttr != null)
                {
                    return nameAttr.Name;
                }
    
                // targeting an async action?
                if (methodInfo.DeclaringType.IsSubclassOf(typeof(AsyncController)))
                {
                    if (methodName.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
                    {
                        return methodName.Substring(0, methodName.Length - "Async".Length);
                    }
                    if (methodName.EndsWith("Completed", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
                           "Nie można wywoływać kompletnych metod.", methodName));
                    }
                }
    
                // fallback
                return methodName;
            }
    
    static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
            {
                ParameterInfo[] parameters = call.Method.GetParameters();
    
                if (parameters.Length > 0)
                {
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        Expression arg = call.Arguments[i];
                        object value = null;
                        ConstantExpression ce = arg as ConstantExpression;
                        if (ce != null)
                        {
                            // If argument is a constant expression, just get the value
                            value = ce.Value;
                        }
                        else
                        {
                            value = CachedExpressionCompiler.Evaluate(arg);
                        }
                        rvd.Add(parameters[i].Name, value);
                    }
                }
            }
    

    希望这可以帮助有类似问题的人。上面的一些代码是我从 mvc2-rtm-sources 获得的,已根据我的需要修改http://aspnet.codeplex.com/releases/view/41742

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-21
      • 2015-03-22
      • 1970-01-01
      • 2019-09-07
      • 1970-01-01
      • 2013-12-23
      • 2013-02-10
      • 1970-01-01
      相关资源
      最近更新 更多