Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
        /// <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());
        }
Ejemplo n.º 3
0
 private static InnerAttributeRoute CreateAttributeRoute(IRouter next, AttributeRouteLinkGenerationEntry entry)
 {
     return CreateAttributeRoute(next, new[] { entry });
 }
Ejemplo n.º 4
0
 private static InnerAttributeRoute CreateAttributeRoute(AttributeRouteLinkGenerationEntry entry)
 {
     return CreateAttributeRoute(new StubRouter(), entry);
 }
Ejemplo n.º 5
0
        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;
 }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        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));
        }
Ejemplo n.º 9
0
 public LinkGenerationMatch(AttributeRouteLinkGenerationEntry entry, bool isFallbackMatch)
 {
     _entry = entry;
     _isFallbackMatch = isFallbackMatch;
 }
Ejemplo n.º 10
0
        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;
        }
Ejemplo n.º 11
0
 private static AttributeRoute CreateAttributeRoute(IRouter next, AttributeRouteLinkGenerationEntry entry)
 {
     return(CreateAttributeRoute(next, new[] { entry }));
 }
Ejemplo n.º 12
0
 private static AttributeRoute CreateAttributeRoute(AttributeRouteLinkGenerationEntry entry)
 {
     return(CreateAttributeRoute(new StubRouter(), entry));
 }