示例#1
0
    public InboundRouteEntry MapInbound(
        IRouter handler,
        RouteTemplate routeTemplate,
        string routeName,
        int order)
    {
        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        if (routeTemplate == null)
        {
            throw new ArgumentNullException(nameof(routeTemplate));
        }

        var entry = new InboundRouteEntry()
        {
            Handler       = handler,
            Order         = order,
            Precedence    = RoutePrecedence.ComputeInbound(routeTemplate),
            RouteName     = routeName,
            RouteTemplate = routeTemplate,
        };

        var constraintBuilder = new RouteConstraintBuilder(_constraintResolver, routeTemplate.TemplateText);

        foreach (var parameter in routeTemplate.Parameters)
        {
            if (parameter.InlineConstraints != null)
            {
                if (parameter.IsOptional)
                {
                    constraintBuilder.SetOptional(parameter.Name);
                }

                foreach (var constraint in parameter.InlineConstraints)
                {
                    constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint);
                }
            }
        }

        entry.Constraints = constraintBuilder.Build();

        entry.Defaults = new RouteValueDictionary();
        foreach (var parameter in entry.RouteTemplate.Parameters)
        {
            if (parameter.DefaultValue != null)
            {
                entry.Defaults.Add(parameter.Name, parameter.DefaultValue);
            }
        }

        InboundEntries.Add(entry);
        return(entry);
    }
示例#2
0
        private InboundRouteEntry MapInbound(RouteTemplate template, Endpoint[] endpoints, int order)
        {
            if (template == null)
            {
                throw new ArgumentNullException(nameof(template));
            }

            var entry = new InboundRouteEntry()
            {
                Precedence    = RoutePrecedence.ComputeInbound(template),
                RouteTemplate = template,
                Order         = order,
                Tag           = endpoints,
            };

            var constraintBuilder = new RouteConstraintBuilder(_constraintFactory, template.TemplateText);

            foreach (var parameter in 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);
                    }
                }
            }

            entry.Constraints = constraintBuilder.Build();

            entry.Defaults = new RouteValueDictionary();
            foreach (var parameter in entry.RouteTemplate.Parameters)
            {
                if (parameter.DefaultValue != null)
                {
                    entry.Defaults.Add(parameter.Name, parameter.DefaultValue);
                }
            }
            return(entry);
        }
示例#3
0
    internal void AddEntry(InboundRouteEntry entry)
    {
        // The url matching tree represents all the routes asociated with a given
        // order. Each node in the tree represents all the different categories
        // a segment can have for which there is a defined inbound route entry.
        // Each node contains a set of Matches that indicate all the routes for which
        // a URL is a potential match. This list contains the routes with the same
        // number of segments and the routes with the same number of segments plus an
        // additional catch all parameter (as it can be empty).
        // For example, for a set of routes like:
        // 'Customer/Index/{id}'
        // '{Controller}/{Action}/{*parameters}'
        //
        // The route tree will look like:
        // Root ->
        //     Literals: Customer ->
        //                   Literals: Index ->
        //                                Parameters: {id}
        //                                                Matches: 'Customer/Index/{id}'
        //     Parameters: {Controller} ->
        //                     Parameters: {Action} ->
        //                                     Matches: '{Controller}/{Action}/{*parameters}'
        //                                     CatchAlls: {*parameters}
        //                                                    Matches: '{Controller}/{Action}/{*parameters}'
        //
        // When the tree router tries to match a route, it iterates the list of url matching trees
        // in ascending order. For each tree it traverses each node starting from the root in the
        // following order: Literals, constrained parameters, parameters, constrained catch all routes, catch alls.
        // When it gets to a node of the same length as the route its trying to match, it simply looks at the list of
        // candidates (which is in precence order) and tries to match the url against it.
        //

        var current = Root;
        var matcher = new TemplateMatcher(entry.RouteTemplate, entry.Defaults);

        for (var i = 0; i < entry.RouteTemplate.Segments.Count; i++)
        {
            var segment = entry.RouteTemplate.Segments[i];
            if (!segment.IsSimple)
            {
                // Treat complex segments as a constrained parameter
                if (current.ConstrainedParameters == null)
                {
                    current.ConstrainedParameters = new UrlMatchingNode(length: i + 1);
                }

                current = current.ConstrainedParameters;
                continue;
            }

            Debug.Assert(segment.Parts.Count == 1);
            var part = segment.Parts[0];
            if (part.IsLiteral)
            {
                if (!current.Literals.TryGetValue(part.Text, out var next))
                {
                    next = new UrlMatchingNode(length: i + 1);
                    current.Literals.Add(part.Text, next);
                }

                current = next;
                continue;
            }

            // We accept templates that have intermediate optional values, but we ignore
            // those values for route matching. For that reason, we need to add the entry
            // to the list of matches, only if the remaining segments are optional. For example:
            // /{controller}/{action=Index}/{id} will be equivalent to /{controller}/{action}/{id}
            // for the purposes of route matching.
            if (part.IsParameter &&
                RemainingSegmentsAreOptional(entry.RouteTemplate.Segments, i))
            {
                current.Matches.Add(new InboundMatch()
                {
                    Entry = entry, TemplateMatcher = matcher
                });
            }

            if (part.IsParameter && part.InlineConstraints.Any() && !part.IsCatchAll)
            {
                if (current.ConstrainedParameters == null)
                {
                    current.ConstrainedParameters = new UrlMatchingNode(length: i + 1);
                }

                current = current.ConstrainedParameters;
                continue;
            }

            if (part.IsParameter && !part.IsCatchAll)
            {
                if (current.Parameters == null)
                {
                    current.Parameters = new UrlMatchingNode(length: i + 1);
                }

                current = current.Parameters;
                continue;
            }

            if (part.IsParameter && part.InlineConstraints.Any() && part.IsCatchAll)
            {
                if (current.ConstrainedCatchAlls == null)
                {
                    current.ConstrainedCatchAlls = new UrlMatchingNode(length: i + 1)
                    {
                        IsCatchAll = true
                    };
                }

                current = current.ConstrainedCatchAlls;
                continue;
            }

            if (part.IsParameter && part.IsCatchAll)
            {
                if (current.CatchAlls == null)
                {
                    current.CatchAlls = new UrlMatchingNode(length: i + 1)
                    {
                        IsCatchAll = true
                    };
                }

                current = current.CatchAlls;
                continue;
            }

            Debug.Fail("We shouldn't get here.");
        }

        current.Matches.Add(new InboundMatch()
        {
            Entry = entry, TemplateMatcher = matcher
        });
        current.Matches.Sort((x, y) =>
        {
            var result = x.Entry.Precedence.CompareTo(y.Entry.Precedence);
            return(result == 0 ? string.Compare(x.Entry.RouteTemplate.TemplateText, y.Entry.RouteTemplate.TemplateText, StringComparison.Ordinal) : result);
        });
    }