/// <summary>
        ///
        /// </summary>
        /// <param name="assemblies"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static RouteContext Create(IEnumerable <Assembly> assemblies, string url)
        {
            RefreshRouteTable(assemblies);

            var routeContext = new Routing.RouteContext(url);

            Routes.Route(routeContext);
            return(new RouteContext()
            {
                Handler = routeContext.Handler,
                Parameters = routeContext.Parameters,
                Segments = routeContext.Segments
            });
        }
Example #2
0
        internal void Match(RouteContext context)
        {
            var pathIndex     = 0;
            var templateIndex = 0;
            Dictionary <string, object> parameters = null;

            // We will iterate over the path segments and the template segments until we have consumed
            // one of them.
            // There are three cases we need to account here for:
            // * Path is shorter than template ->
            //   * This can match only if we have t-p optional parameters at the end.
            // * Path and template have the same number of segments
            //   * This can happen when the catch-all segment matches 1 segment
            //   * This can happen when an optional parameter has been specified.
            //   * This can happen when the route only contains literals and parameters.
            // * Path is longer than template -> This can only match if the parameter has a catch-all at the end.
            //   * We still need to iterate over all the path segments if the catch-all is constrained.
            //   * We still need to iterate over all the template/path segments before the catch-all
            while (pathIndex < context.Segments.Length && templateIndex < Template.Segments.Length)
            {
                var pathSegment     = context.Segments[pathIndex];
                var templateSegment = Template.Segments[templateIndex];

                var matches = templateSegment.Match(pathSegment, out var match);
                if (!matches)
                {
                    // A constraint or literal didn't match
                    return;
                }

                if (!templateSegment.IsCatchAll)
                {
                    // We were dealing with a literal or a parameter, so just advance both cursors.
                    pathIndex++;
                    templateIndex++;

                    if (templateSegment.IsParameter)
                    {
                        parameters ??= new(StringComparer.OrdinalIgnoreCase);
                        parameters[templateSegment.Value] = match;
                    }
                }
                else
                {
                    if (templateSegment.Constraints.Length == 0)
                    {
                        // Unconstrained catch all, we can stop early
                        parameters ??= new(StringComparer.OrdinalIgnoreCase);
                        parameters[templateSegment.Value] = string.Join('/', context.Segments, pathIndex, context.Segments.Length - pathIndex);

                        // Mark the remaining segments as consumed.
                        pathIndex = context.Segments.Length;

                        // Catch-alls are always last.
                        templateIndex++;

                        // We are done, so break out of the loop.
                        break;
                    }
                    else
                    {
                        // For constrained catch-alls, we advance the path index but keep the template index on the catch-all.
                        pathIndex++;
                        if (pathIndex == context.Segments.Length)
                        {
                            parameters ??= new(StringComparer.OrdinalIgnoreCase);
                            parameters[templateSegment.Value] = string.Join('/', context.Segments, templateIndex, context.Segments.Length - templateIndex);

                            // This is important to signal that we consumed the entire template.
                            templateIndex++;
                        }
                    }
                }
            }

            var hasRemainingOptionalSegments = templateIndex < Template.Segments.Length &&
                                               RemainingSegmentsAreOptional(pathIndex, Template.Segments);

            if ((pathIndex == context.Segments.Length && templateIndex == Template.Segments.Length) || hasRemainingOptionalSegments)
            {
                if (hasRemainingOptionalSegments)
                {
                    parameters ??= new Dictionary <string, object>(StringComparer.Ordinal);
                    AddDefaultValues(parameters, templateIndex, Template.Segments);
                }
                if (UnusedRouteParameterNames?.Length > 0)
                {
                    parameters ??= new Dictionary <string, object>(StringComparer.Ordinal);
                    for (var i = 0; i < UnusedRouteParameterNames.Length; i++)
                    {
                        parameters[UnusedRouteParameterNames[i]] = null;
                    }
                }
                context.Handler    = Handler;
                context.Parameters = parameters;
            }
        }