private static AttributeRouteLinkGenerationEntry CreateGenerationEntry(string template, object requiredValues) { var constraintResolver = CreateConstraintResolver(); var entry = new AttributeRouteLinkGenerationEntry(); entry.Template = TemplateParser.Parse(template, constraintResolver); var defaults = entry.Template.Parameters .Where(p => p.DefaultValue != null) .ToDictionary(p => p.Name, p => p.DefaultValue); var constraints = entry.Template.Parameters .Where(p => p.InlineConstraint != null) .ToDictionary(p => p.Name, p => p.InlineConstraint); entry.Constraints = constraints; entry.Defaults = defaults; entry.Binder = new TemplateBinder(entry.Template, defaults); entry.Precedence = AttributeRoutePrecedence.Compute(entry.Template); entry.RequiredLinkValues = new RouteValueDictionary(requiredValues); entry.RouteGroup = template; return(entry); }
/// <summary> /// Creates a new <see cref="InnerAttributeRoute"/>. /// </summary> /// <param name="next">The next router. Invoked when a route entry matches.</param> /// <param name="entries">The set of route entries.</param> public InnerAttributeRoute( [NotNull] IRouter next, [NotNull] IEnumerable <AttributeRouteMatchingEntry> matchingEntries, [NotNull] IEnumerable <AttributeRouteLinkGenerationEntry> linkGenerationEntries, [NotNull] ILogger logger, [NotNull] ILogger constraintLogger, int version) { _next = next; _logger = logger; _constraintLogger = constraintLogger; Version = version; // Order all the entries by order, then precedence, and then finally by template in order to provide // a stable routing and link generation order for templates with same order and precedence. // We use ordinal comparison for the templates because we only care about them being exactly equal and // we don't want to make any equivalence between templates based on the culture of the machine. _matchingRoutes = matchingEntries .OrderBy(o => o.Order) .ThenBy(e => e.Precedence) .ThenBy(e => e.Route.RouteTemplate, StringComparer.Ordinal) .Select(e => e.Route) .ToArray(); var namedEntries = new Dictionary <string, AttributeRouteLinkGenerationEntry>( StringComparer.OrdinalIgnoreCase); foreach (var entry in linkGenerationEntries) { // Skip unnamed entries if (entry.Name == null) { continue; } // We only need to keep one AttributeRouteLinkGenerationEntry per route template // so in case two entries have the same name and the same template we only keep // the first entry. AttributeRouteLinkGenerationEntry namedEntry = null; if (namedEntries.TryGetValue(entry.Name, out namedEntry) && !namedEntry.TemplateText.Equals(entry.TemplateText, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException( Resources.FormatAttributeRoute_DifferentLinkGenerationEntries_SameName(entry.Name), "linkGenerationEntries"); } else if (namedEntry == null) { namedEntries.Add(entry.Name, entry); } } _namedEntries = namedEntries; // The decision tree will take care of ordering for these entries. _linkGenerationTree = new LinkGenerationDecisionTree(linkGenerationEntries.ToArray()); }
private static InnerAttributeRoute CreateAttributeRoute(IRouter next, AttributeRouteLinkGenerationEntry entry) { return CreateAttributeRoute(next, new[] { entry }); }
private static InnerAttributeRoute CreateAttributeRoute(AttributeRouteLinkGenerationEntry entry) { return CreateAttributeRoute(new StubRouter(), entry); }
private static AttributeRouteLinkGenerationEntry CreateGenerationEntry( string template, object requiredValues, int order = 0, string name = null) { var constraintResolver = CreateConstraintResolver(); var entry = new AttributeRouteLinkGenerationEntry(); entry.TemplateText = template; entry.Template = TemplateParser.Parse(template); var defaults = entry.Template.Parameters .Where(p => p.DefaultValue != null) .ToDictionary(p => p.Name, p => p.DefaultValue); var constraintBuilder = new RouteConstraintBuilder(CreateConstraintResolver(), template); foreach (var parameter in entry.Template.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); } } } var constraints = constraintBuilder.Build(); entry.Constraints = constraints; entry.Defaults = defaults; entry.Binder = new TemplateBinder(entry.Template, defaults); entry.Order = order; entry.Precedence = AttributeRoutePrecedence.Compute(entry.Template); entry.RequiredLinkValues = new RouteValueDictionary(requiredValues); entry.RouteGroup = CreateRouteGroup(order, template); entry.Name = name; return entry; }
private AttributeRouteLinkGenerationEntry CreateEntry(object requiredValues) { var entry = new AttributeRouteLinkGenerationEntry(); entry.RequiredLinkValues = new RouteValueDictionary(requiredValues); return entry; }
private VirtualPathData GenerateVirtualPath(VirtualPathContext context, AttributeRouteLinkGenerationEntry entry) { // In attribute the context includes the values that are used to select this entry - typically // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't // want to pass these to the link generation code, or else they will end up as query parameters. // // So, we need to exclude from here any values that are 'required link values', but aren't // parameters in the template. // // Ex: // template: api/Products/{action} // required values: { id = "5", action = "Buy", Controller = "CoolProducts" } // // result: { id = "5", action = "Buy" } var inputValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); foreach (var kvp in context.Values) { if (entry.RequiredLinkValues.ContainsKey(kvp.Key)) { var parameter = entry.Template.Parameters .FirstOrDefault(p => string.Equals(p.Name, kvp.Key, StringComparison.OrdinalIgnoreCase)); if (parameter == null) { continue; } } inputValues.Add(kvp.Key, kvp.Value); } var bindingResult = entry.Binder.GetValues(context.AmbientValues, inputValues); if (bindingResult == null) { // A required parameter in the template didn't get a value. return null; } var matched = RouteConstraintMatcher.Match( entry.Constraints, bindingResult.CombinedValues, context.Context, this, RouteDirection.UrlGeneration, _constraintLogger); if (!matched) { // A constraint rejected this link. return null; } // These values are used to signal to the next route what we would produce if we round-tripped // (generate a link and then parse). In MVC the 'next route' is typically the MvcRouteHandler. var providedValues = new Dictionary<string, object>( bindingResult.AcceptedValues, StringComparer.OrdinalIgnoreCase); providedValues.Add(AttributeRouting.RouteGroupKey, entry.RouteGroup); var childContext = new VirtualPathContext(context.Context, context.AmbientValues, context.Values) { ProvidedValues = providedValues, }; var pathData = _next.GetVirtualPath(childContext); if (pathData != null) { // If path is non-null then the target router short-circuited, we don't expect this // in typical MVC scenarios. return pathData; } else if (!childContext.IsBound) { // The target router has rejected these values. We don't expect this in typical MVC scenarios. return null; } var path = entry.Binder.BindValues(bindingResult.AcceptedValues); if (path == null) { return null; } return new VirtualPathData(this, path); }
private VirtualPathData GenerateVirtualPath(VirtualPathContext context, AttributeRouteLinkGenerationEntry entry) { // In attribute the context includes the values that are used to select this entry - typically // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't // want to pass these to the link generation code, or else they will end up as query parameters. // // So, we need to exclude from here any values that are 'required link values', but aren't // parameters in the template. // // Ex: // template: api/Products/{action} // required values: { id = "5", action = "Buy", Controller = "CoolProducts" } // // result: { id = "5", action = "Buy" } var inputValues = new Dictionary <string, object>(StringComparer.OrdinalIgnoreCase); foreach (var kvp in context.Values) { if (entry.RequiredLinkValues.ContainsKey(kvp.Key)) { var parameter = entry.Template.Parameters .FirstOrDefault(p => string.Equals(p.Name, kvp.Key, StringComparison.OrdinalIgnoreCase)); if (parameter == null) { continue; } } inputValues.Add(kvp.Key, kvp.Value); } var bindingResult = entry.Binder.GetValues(context.AmbientValues, inputValues); if (bindingResult == null) { // A required parameter in the template didn't get a value. return(null); } var matched = RouteConstraintMatcher.Match( entry.Constraints, bindingResult.CombinedValues, context.Context, this, RouteDirection.UrlGeneration, _constraintLogger); if (!matched) { // A constraint rejected this link. return(null); } // These values are used to signal to the next route what we would produce if we round-tripped // (generate a link and then parse). In MVC the 'next route' is typically the MvcRouteHandler. var providedValues = new Dictionary <string, object>( bindingResult.AcceptedValues, StringComparer.OrdinalIgnoreCase); providedValues.Add(AttributeRouting.RouteGroupKey, entry.RouteGroup); var childContext = new VirtualPathContext(context.Context, context.AmbientValues, context.Values) { ProvidedValues = providedValues, }; var pathData = _next.GetVirtualPath(childContext); if (pathData != null) { // If path is non-null then the target router short-circuited, we don't expect this // in typical MVC scenarios. return(pathData); } else if (!childContext.IsBound) { // The target router has rejected these values. We don't expect this in typical MVC scenarios. return(null); } var path = entry.Binder.BindValues(bindingResult.AcceptedValues); if (path == null) { return(null); } return(new VirtualPathData(this, path)); }
public LinkGenerationMatch(AttributeRouteLinkGenerationEntry entry, bool isFallbackMatch) { _entry = entry; _isFallbackMatch = isFallbackMatch; }
private static AttributeRouteLinkGenerationEntry CreateGenerationEntry(string template, object requiredValues) { var constraintResolver = CreateConstraintResolver(); var entry = new AttributeRouteLinkGenerationEntry(); entry.Template = TemplateParser.Parse(template, constraintResolver); var defaults = entry.Template.Parameters .Where(p => p.DefaultValue != null) .ToDictionary(p => p.Name, p => p.DefaultValue); var constraints = entry.Template.Parameters .Where(p => p.InlineConstraint != null) .ToDictionary(p => p.Name, p => p.InlineConstraint); entry.Constraints = constraints; entry.Defaults = defaults; entry.Binder = new TemplateBinder(entry.Template, defaults); entry.Precedence = AttributeRoutePrecedence.Compute(entry.Template); entry.RequiredLinkValues = new RouteValueDictionary(requiredValues); entry.RouteGroup = template; return entry; }
private static AttributeRoute CreateAttributeRoute(IRouter next, AttributeRouteLinkGenerationEntry entry) { return(CreateAttributeRoute(next, new[] { entry })); }
private static AttributeRoute CreateAttributeRoute(AttributeRouteLinkGenerationEntry entry) { return(CreateAttributeRoute(new StubRouter(), entry)); }