在IRouteConstraint上完全忽略RouteAttribute的Order

时间:2018-04-17 03:35:44

标签: asp.net-mvc asp.net-mvc-5 attributerouting

我们有一个IRouteConstraint得到的检查远远超过应有的检查。经过进一步测试后,Order上的[Route]似乎被路由约束忽略了。

例如,如果我有以下约束:

public class TestConstraint : IRouteConstraint {
    public bool Match(
        HttpContextBase httpContext,
        Route route,
        string parameterName,
        RouteValueDictionary values,
        RouteDirection routeDirection
    ) {
        Debug.WriteLine("TestConstraint");
        return true;
    }
}

然后连线:

constraintResolver.ConstraintMap.Add("testConstraint", typeof(TestConstraint));

并有以下路线:

public partial class HomeController {
    [Route("test/0", Order = 1)]
    public ActionResult Test0() {
        return Content("Test0");
    }

    [Route("{someParam}/{test:testConstraint}", Order = 10)]
    public ActionResult Test1() {
        return Content("Test1");
    }
}

然后请求http://localhost/test/0,它会返回正确的内容(Test0),但TestContraint.Match()仍在执行。

我认为只有在RouteTable遇到路由后才会执行路由约束,但它似乎在每个请求上运行它,可以匹配[Route]图案。

如果它有所不同,我们使用的是ASP.NET MVC v5.2.4。

1 个答案:

答案 0 :(得分:2)

在ASP.NET MVC管道中,路由阶段和调用控制器操作的选择阶段是分开的。在路由阶段,您不能只选择第一个匹配操作并停止进一步查找。找到的行动(严格来说,方法)可以在以后阶段过滤。例如,它可能不满足应用的操作选择器(例如NonAction属性)。

这就是为什么基本动作选择算法如下:

  1. 通过已配置的路由传递请求URL并选择所有匹配的操作。
  2. 通过操作选择器传递所有匹配的操作,过滤掉不匹配的操作。
  3. 按路线顺序订购候选人行动。
  4. 现在有以下选项:

    1. 未找到匹配的操作。请求结果为404错误。
    2. 多个匹配操作共享相同的最高顺序。抛出异常("当前请求在以下操作方法之间不明确......")。
    3. 正好一个匹配的动作具有最高顺序(或者一个动作完全匹配)。选择并执行该操作。
    4. 如果您对对应的ASP.NET MVC源代码感兴趣,可以参考以下内容:

      IRouteConstraint.Match()中的ProcessConstraint()方法调用

      System.Web.Routing.Route。调用堆栈中最近的方法,在路由集合级别上运行,是GetRouteData()类中的System.Web.Mvc.Routing.RouteCollectionRoute方法:

      enter image description here

      以下是source code

      public override RouteData GetRouteData(HttpContextBase httpContext)
      {
          List<RouteData> matches = new List<RouteData>();
          foreach (RouteBase route in _subRoutes)
          {
              var match = route.GetRouteData(httpContext);
              if (match != null)
              {
                  matches.Add(match);
              }
          }
      
          return CreateDirectRouteMatch(this, matches);
      }
      

      如您所见,找到匹配的路线时,循环不会中断。

      应用操作选择器的代码,执行排序并选择动作候选位于DirectRouteCandidate.SelectBestCandidate()source code):

      public static DirectRouteCandidate SelectBestCandidate(List<DirectRouteCandidate> candidates, ControllerContext controllerContext)
      {
          Debug.Assert(controllerContext != null);
          Debug.Assert(candidates != null);
      
          // These filters will allow actions to opt-out of execution via the provided public extensibility points.
          List<DirectRouteCandidate> filteredByActionName = ApplyActionNameFilters(candidates, controllerContext);
          List<DirectRouteCandidate> applicableCandidates = ApplyActionSelectors(filteredByActionName, controllerContext);
      
          // At this point all of the remaining actions are applicable - now we're just trying to find the
          // most specific match.
          //
          // Order is first, because it's the 'override' to our algorithm
          List<DirectRouteCandidate> filteredByOrder = FilterByOrder(applicableCandidates);
          List<DirectRouteCandidate> filteredByPrecedence = FilterByPrecedence(filteredByOrder);
      
          if (filteredByPrecedence.Count == 0)
          {
              return null;
          }
          else if (filteredByPrecedence.Count == 1)
          {
              return filteredByPrecedence[0];
          }
          else
          {
              throw CreateAmbiguiousMatchException(candidates);
          }
      }