2012-11-16 61 views
3

作为this question的后续,我试图实现自定义路由约束,以动态修改特定路由的RouteValueDictionary动态修改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方法被调用,它将如果动作返回true。在url中指定有两个两个整数参数,无论o f实际参数名称。)

那么,有没有一种方法可以覆盖RouteValueDictionary的这个请求?或者有没有其他方法可以做到这一点,我错过了?

+1

不是ASP的专家。NET MVC,但我不知道路由字典是否是共享资源,无论是C#中的static或VB.NET中的Shared?如果是这样,那么当您修改它时,您可能需要使用锁定,因为其他请求可能会在您更改它的同时尝试读取它。 –

回答

3

如果我理解正确的是这样一个问题:

那么,有没有一种方法,我可以覆盖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 ...

+0

啊......我不知道这是那么简单!我收到的错误是“收集只读”,所以我认为任何修改都行不通=显然是一个错误的假设。无论如何,它的功能就像一个魅力 - 我会在我的另一个问题上将整个代码发布到我的解决方案。谢谢你的帮助! – 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}); 

正如我在回答说你earlier question,你不应该担心在控制器操作的参数名称。给那些过于有意义的名字会把你打结。