Beispiel #1
0
        public Route(string url, string areaName, Type handlerType, bool localised, bool personalised)
        {
            this.areaName     = areaName;
            this.HandlerType  = handlerType;
            this.Localised    = localised;
            this.Personalised = personalised;

            if (string.IsNullOrEmpty(url))
            {
                throw new ArgumentException("URL must have a value", nameof(url));
            }

            if (!url.StartsWith("/"))
            {
                throw new ArgumentException("URL `" + url + "` must start with a /", nameof(url));
            }

            this.Path        = url;
            this.parsedRoute = ParsedRoute.Parse(url);

            if (localised)
            {
                if (url != "/")
                {
                    this.parsedLocalisedRoute = ParsedRoute.Parse("/{regionCode}/{cultureCode}/" + url.TrimStart('/'));
                }
                else
                {
                    this.parsedLocalisedRoute = ParsedRoute.Parse("/{regionCode}/{cultureCode}");
                }
            }
        }
Beispiel #2
0
        public static ParsedRoute Parse(string url)
        {
            var parsedRoute = new ParsedRoute();

            parsedRoute.Url            = url;
            parsedRoute.parameterNames = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase);

            if (!string.IsNullOrEmpty(url))
            {
                if (url.IndexOf('?') >= 0)
                {
                    throw new ArgumentException("Url must not contain '?'");
                }
            }
            else
            {
                parsedRoute.segments = new PatternSegment[0];
                parsedRoute.tokens   = new PatternToken[0];
                return(parsedRoute);
            }

            var parts      = url.TrimStart('/').Split('/');
            var partsCount = parsedRoute.segmentCount = parts.Length;
            var allTokens  = new List <PatternToken>();

            parsedRoute.segments = new PatternSegment[partsCount];

            for (var i = 0; i < partsCount; i++)
            {
                if (parsedRoute.hasCatchAllSegment)
                {
                    throw new ArgumentException("A catch-all parameter can only appear as the last segment of the route URL");
                }

                var catchAlls  = 0;
                var part       = parts[i];
                var partLength = part.Length;
                var tokens     = new List <PatternToken>();

                if (partLength == 0 && i > 0 && i < partsCount - 1)
                {
                    throw new ArgumentException("Consecutive URL segment separators '/' are not allowed");
                }

                if (part.IndexOf("{}") != -1)
                {
                    throw new ArgumentException("Empty URL parameter name is not allowed");
                }

                if (i > 0)
                {
                    allTokens.Add(null);
                }

                PatternToken tmpToken;
                if (part.IndexOfAny(placeholderDelimiters) == -1)
                {
                    // no placeholders here, short-circuit it
                    tmpToken = new PatternToken(PatternTokenType.Literal, part);
                    tokens.Add(tmpToken);
                    allTokens.Add(tmpToken);
                    parsedRoute.segments[i].AllTokensAreLiteral = true;
                    parsedRoute.segments[i].Tokens = tokens;
                    continue;
                }

                parsedRoute.IsDynamic = true;

                var currentIndex = 0;
                var allLiteral   = true;
                while (currentIndex < partLength)
                {
                    var openParameterIndex = part.IndexOf('{', currentIndex);
                    if (openParameterIndex >= partLength - 2)
                    {
                        throw new ArgumentException("Unterminated URL parameter. It must contain matching '}'");
                    }

                    // No open tag, must be a literal
                    if (openParameterIndex < 0)
                    {
                        if (part.IndexOf('}', currentIndex) >= currentIndex)
                        {
                            throw new ArgumentException("Unmatched URL parameter closer '}'. A corresponding '{' must precede");
                        }
                        var tmp = part.Substring(currentIndex);
                        tmpToken = new PatternToken(PatternTokenType.Literal, tmp);
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                        break;
                    }

                    // parameter found later in the segment, this bit is a literal
                    if (currentIndex == 0 && openParameterIndex > 0)
                    {
                        tmpToken = new PatternToken(PatternTokenType.Literal, part.Substring(0, openParameterIndex));
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                    }

                    var end  = part.IndexOf('}', openParameterIndex + 1);
                    var next = part.IndexOf('{', openParameterIndex + 1);

                    if (end < 0 || next >= 0 && next < end)
                    {
                        throw new ArgumentException($"Unterminated URL parameter `{url}`. It must contain matching '}}'");
                    }

                    if (next == end + 1)
                    {
                        throw new ArgumentException($"Two consecutive URL parameters are not allowed `{url}`. Split into a different segment by '/', or a literal string.");
                    }

                    if (next == -1)
                    {
                        next = partLength;
                    }

                    var token = part.Substring(openParameterIndex + 1, end - openParameterIndex - 1);
                    PatternTokenType type;
                    if (token[0] == '*')
                    {
                        catchAlls++;
                        parsedRoute.hasCatchAllSegment = true;
                        type  = PatternTokenType.CatchAll;
                        token = token.Substring(1);
                    }
                    else
                    {
                        type = PatternTokenType.Standard;
                    }

                    if (!parsedRoute.parameterNames.ContainsKey(token))
                    {
                        parsedRoute.parameterNames.Add(token, true);
                    }

                    tmpToken = new PatternToken(type, token);
                    tokens.Add(tmpToken);
                    allTokens.Add(tmpToken);
                    allLiteral = false;

                    if (end < partLength - 1)
                    {
                        token    = part.Substring(end + 1, next - end - 1);
                        tmpToken = new PatternToken(PatternTokenType.Literal, token);
                        tokens.Add(tmpToken);
                        allTokens.Add(tmpToken);
                        end += token.Length;
                    }

                    if (catchAlls > 1 || (catchAlls == 1 && tokens.Count > 1))
                    {
                        throw new ArgumentException("A path segment that contains more than one section, such as a literal section or a parameter, cannot contain a catch-all parameter.");
                    }
                    currentIndex = end + 1;
                }

                parsedRoute.segments[i].AllTokensAreLiteral = allLiteral;
                parsedRoute.segments[i].Tokens = tokens;
            }

            if (allTokens.Count > 0)
            {
                parsedRoute.tokens = allTokens.ToArray();
            }

            return(parsedRoute);
        }