public void AttributeRoute_GetEntries_CreatesMatchingEntry_WithDefault() { // Arrange var actions = new List <ActionDescriptor>() { new ActionDescriptor() { AttributeRouteInfo = new AttributeRouteInfo() { Template = "api/Blog/{*slug=hello}", Name = "BLOG_INDEX", Order = 17, }, RouteConstraints = new List <RouteDataActionConstraint>() { new RouteDataActionConstraint(TreeRouter.RouteGroupKey, "1"), }, RouteValueDefaults = new Dictionary <string, object>() { { "controller", "Blog" }, { "action", "Index" }, }, }, }; var actionDescriptorProvider = CreateActionDescriptorProvider(actions); var handler = CreateHandler().Object; var route = CreateRoute(handler, actionDescriptorProvider.Object); // Act var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( entries.MatchingEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{*slug=hello}", e.RouteTemplate.TemplateText); Assert.Same(handler, e.Target); Assert.Collection( e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair <string, object>(TreeRouter.RouteGroupKey, "1"), kvp), kvp => Assert.Equal(new KeyValuePair <string, object>("slug", "hello"), kvp)); Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); }
private static RouteInfo GetRouteInfo( IInlineConstraintResolver constraintResolver, Dictionary <string, RouteTemplate> templateCache, ActionDescriptor action) { var constraint = action.RouteConstraints .FirstOrDefault(c => c.RouteKey == TreeRouter.RouteGroupKey); if (constraint == null || constraint.KeyHandling != RouteKeyHandling.RequireKey || constraint.RouteValue == null) { // This can happen if an ActionDescriptor has a route template, but doesn't have one of our // special route group constraints. This is a good indication that the user is using a 3rd party // routing system, or has customized their ADs in a way that we can no longer understand them. // // We just treat this case as an 'opt-out' of our attribute routing system. return(null); } var routeInfo = new RouteInfo() { ActionDescriptor = action, RouteGroup = constraint.RouteValue, RouteTemplate = action.AttributeRouteInfo.Template, }; try { RouteTemplate parsedTemplate; if (!templateCache.TryGetValue(action.AttributeRouteInfo.Template, out parsedTemplate)) { // Parsing with throw if the template is invalid. parsedTemplate = TemplateParser.Parse(action.AttributeRouteInfo.Template); templateCache.Add(action.AttributeRouteInfo.Template, parsedTemplate); } routeInfo.ParsedTemplate = parsedTemplate; } catch (Exception ex) { routeInfo.ErrorMessage = ex.Message; return(routeInfo); } foreach (var kvp in action.RouteValueDefaults) { foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (string.Equals(kvp.Key, parameter.Name, StringComparison.OrdinalIgnoreCase)) { routeInfo.ErrorMessage = Resources.FormatAttributeRoute_CannotContainParameter( routeInfo.RouteTemplate, kvp.Key, kvp.Value); return(routeInfo); } } } routeInfo.Order = action.AttributeRouteInfo.Order; routeInfo.MatchPrecedence = RoutePrecedence.ComputeMatched(routeInfo.ParsedTemplate); routeInfo.GenerationPrecedence = RoutePrecedence.ComputeGenerated(routeInfo.ParsedTemplate); routeInfo.Name = action.AttributeRouteInfo.Name; var constraintBuilder = new RouteConstraintBuilder(constraintResolver, routeInfo.RouteTemplate); foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var inlineConstraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, inlineConstraint.Constraint); } } } routeInfo.Constraints = constraintBuilder.Build(); routeInfo.Defaults = new RouteValueDictionary(); foreach (var parameter in routeInfo.ParsedTemplate.Parameters) { if (parameter.DefaultValue != null) { routeInfo.Defaults.Add(parameter.Name, parameter.DefaultValue); } } return(routeInfo); }