动态修改RouteValueDictionary

4
作为对此问题的跟进,我正在尝试实现自定义路由约束,以动态修改特定路由的RouteValueDictionary
我已经完成了95%的工作:根据提供的参数模式匹配url中指定的操作,可以匹配任何路由。这个过程的最后一步是覆盖RouteValueDictionary,将键重命名为控制器操作的适当参数。
以下是我的代码相关片段(整个类几乎有300行,因此我不想添加全部内容,但如果需要,我可以添加):
public class CustomRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string paramName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (routeDirection.Equals(RouteDirection.UrlGeneration)) {
            return false;
        }

        //this will grab all of the "actual" parameters out of the RVD, excluding action and controller 
        Dictionary<string, object> unMappedList = values.Where(x => x.Key.Contains("param")).OrderBy(xi => xi.Key).ToDictionary(
            kvp => kvp.Key, kvp => kvp.Value);

        string controller = values["controller"] as string;
        string action = values["action"] as string;

        //this method tries to find the controller using reflection
        Type cont = TryFindController(controller);

        if (cont != null) {
            MethodInfo actionMethod = cont.GetMethod(action);

            if (actionMethod != null) {
                ParameterInfo[] methodParameters = actionMethod.GetParameters();

                //this method validates that the parameters of the action that was found match the expected parameters passed in the custom constraint; it also performs type conversion
                if (validateParameters(methodParameters, unMappedList)) {
                    for (int i = 0; i < methodParameters.Length; i++) {
                        values.First(x => x.Key == unMappedList.ElementAt(i).Key).Key = methodParameters.ElementAt(i).Name; 
                        //above doesn't work, but even if it did it wouldn't do the right thing

                        return true;
                    }
                }
            }
        }

        return false;
    }
}

以下是我如何使用自定义约束的方法:

routes.MapRoute(
    name: "Default2",
    url: "{controller}/{action}/{param1}-{param2}",
    defaults: new { controller = "Admin", action = "Index" },
    constraints: new { lang = new CustomRouteConstraint(new RoutePatternCollection( new List<ParamType> { ParamType.INT, ParamType.INT })) }
);

我传递给构造函数的基本上是在说“要查找的参数模式是两个整数”。所以当调用Match方法时,如果url中指定的操作具有两个整数参数(而不管实际参数名称),它将返回true。 因此,我能否覆盖此请求的RouteValueDictionary?还是我错过了其他方法可以做到这一点?

1
虽然我不是ASP.NET MVC的专家,但我想知道路由字典是否是一个共享资源,无论是C#中的static还是VB.NET中的Shared?如果是这样的话,那么当您修改它时,其他请求可能正在尝试同时读取它,因此您需要使用锁定。 - John Saunders
2个回答

4
如果我理解问题正确的话:
那么,有没有一种方法可以覆盖此请求的RouteValueDictionary?或者我错过了其他方法吗?
我采用了您的解决方案,并更改了这些行以替换参数名称:
for (int i = 0; i < methodParameters.Length; i++)
{
    // I. instead of this

    //values.First(x => x.Key == unMappedList.ElementAt(i).Key)
    //    .Key = methodParameters.ElementAt(i).Name; 
    //above doesn't work, but even if it did it wouldn't do the right thing

    // II. do this (not so elegant but working;)
    var key = unMappedList.ElementAt(i).Key;
    var value = values[key];
    values.Remove(key);
    values.Add(methodParameters.ElementAt(i).Name, value);

    // III. THIS is essential! if statement must be moved after the for loop
    //return true;
}
return true; // here we can return true

注意: 我喜欢你的方法。有时候,扩展/调整路由比去代码中“修复不正确的参数名称”更重要。当然,它们很可能是不正确的。

这就是关注点分离的威力。

  • URL地址是一个地方。
  • 控制器、操作和参数名称是另一个地方。
  • 强大的自定义路由...

...作为唯一正确的处理地点。这就是关注点分离。而不是调整操作名称以服务于URL...


啊...我完全不知道它是那么简单的!我收到的错误信息说“集合是只读的”,所以我假设任何修改都不会起作用 = 显然是一个错误的假设。无论如何,现在它像魔法一样运行 - 我将在我的其他问题中发布整个代码解决方案。非常感谢您的帮助! - Mansfield

1

您可以使用正则表达式轻松限制参数为正整数:

private const string IdRouteConstraint = @"^\d+$";

routes.MapRoute(
   name: "Default2",
   url: "{controller}/{action}/{param1}-{param2}",
   defaults: new { controller = "Admin", action = "Index" },
   constraints: new { param1 = IdRouteConstraint, param2 = IdRouteConstraint});

正如我在回答你之前的问题时所说,你不应该担心控制器操作中的参数名称。给这些过于有意义的名称会让你陷入困境。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接