Beispiel #1
0
        /// <summary>
        /// Generate routes given a collection of MethodInfo objects and templates that should call those methods
        /// </summary>
        /// <param name="templatesByHandler">Templates that should route to each handler</param>
        internal static MqttRouteTable Create(Dictionary <MethodInfo, string[]> templatesByHandler)
        {
            var routes = new List <MqttRoute>();

            foreach (var keyValuePair in templatesByHandler)
            {
                var parsedTemplates = keyValuePair.Value.Select(v => TemplateParser.ParseTemplate(v)).ToArray();

                var allRouteParameterNames = parsedTemplates
                                             .SelectMany(GetParameterNames)
                                             .Distinct(StringComparer.OrdinalIgnoreCase)
                                             .ToArray();

                foreach (var parsedTemplate in parsedTemplates)
                {
                    var unusedRouteParameterNames = allRouteParameterNames
                                                    .Except(GetParameterNames(parsedTemplate), StringComparer.OrdinalIgnoreCase)
                                                    .ToArray();

                    var entry = new MqttRoute(parsedTemplate, keyValuePair.Key, unusedRouteParameterNames);

                    routes.Add(entry);
                }
            }

            return(new MqttRouteTable(routes.OrderBy(id => id, RoutePrecedence).ToArray()));
        }
Beispiel #2
0
        /// <summary>
        /// Route precedence algorithm. We collect all the routes and sort them from most specific to less specific. The
        /// specificity of a route is given by the specificity of its segments and the position of those segments in the route.
        /// * A literal segment is more specific than a parameter segment.
        /// * A parameter segment with more constraints is more specific than one with fewer constraints
        /// * Segment earlier in the route are evaluated before segments later in the route. For example: /Literal is
        /// more specific than /Parameter /Route/With/{parameter} is more specific than /{multiple}/With/{parameters}
        /// /Product/{id:int} is more specific than /Product/{id}
        ///
        /// Routes can be ambiguous if: They are composed of literals and those literals have the same values (case
        /// insensitive) They are composed of a mix of literals and parameters, in the same relative order and the
        /// literals have the same values. For example:
        /// * /literal and /Literal /{parameter}/literal and /{something}/literal /{parameter:constraint}/literal and /{something:constraint}/literal
        ///
        /// To calculate the precedence we sort the list of routes as follows:
        /// * Shorter routes go first.
        /// * A literal wins over a parameter in precedence.
        /// * For literals with different values (case insensitive) we choose the lexical order
        /// * For parameters with different numbers of constraints, the one with more wins If we get to the end of the
        /// comparison routing we've detected an ambiguous pair of routes.
        /// </summary>
        internal static int RouteComparison(MqttRoute x, MqttRoute y)
        {
            if (ReferenceEquals(x, y))
            {
                return(0);
            }

            var xTemplate = x.Template;
            var yTemplate = y.Template;

            if (xTemplate.Segments.Count() != y.Template.Segments.Count())
            {
                return(xTemplate.Segments.Count() < y.Template.Segments.Count() ? -1 : 1);
            }
            else
            {
                for (var i = 0; i < xTemplate.Segments.Count(); i++)
                {
                    var xSegment = xTemplate.Segments[i];
                    var ySegment = yTemplate.Segments[i];

                    if (!xSegment.IsParameter && ySegment.IsParameter)
                    {
                        return(-1);
                    }

                    if (xSegment.IsParameter && !ySegment.IsParameter)
                    {
                        return(1);
                    }

                    if (xSegment.IsParameter)
                    {
                        // Always favor non-optional parameters over optional ones
                        if (!xSegment.IsOptional && ySegment.IsOptional)
                        {
                            return(-1);
                        }

                        if (xSegment.IsOptional && !ySegment.IsOptional)
                        {
                            return(1);
                        }

                        if (xSegment.Constraints.Length > ySegment.Constraints.Length)
                        {
                            return(-1);
                        }
                        else if (xSegment.Constraints.Length < ySegment.Constraints.Length)
                        {
                            return(1);
                        }
                    }
                    else
                    {
                        var comparison = string.Compare(xSegment.Value, ySegment.Value, StringComparison.OrdinalIgnoreCase);

                        if (comparison != 0)
                        {
                            return(comparison);
                        }
                    }
                }

                throw new InvalidOperationException($@"The following routes are ambiguous:
'{x.Template.TemplateText}' in '{x.Handler.DeclaringType.FullName}.{x.Handler.Name}'
'{y.Template.TemplateText}' in '{y.Handler.DeclaringType.FullName}.{y.Handler.Name}'
");
            }
        }