Example #1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="userValues"></param>
        /// <returns></returns>
        public string BuildPath(RouteValues userValues)
        {
            userValues = userValues ?? new RouteValues();

            if (this.parameterNames.Any(de => !userValues.ContainsKey(de.Key)))
            {
                log.Error("User route values did not contain a necessary parameter to build path. Url: {url}, User values: {userValues}", this.Url, userValues.Keys);
                return(null);
            }

            var urlBuilder = new StringBuilder();

            int tokensCount = this.tokens.Length - 1;

            for (int i = tokensCount; i >= 0; i--)
            {
                PatternToken token = this.tokens[i];
                if (token == null)
                {
                    if (i < tokensCount && urlBuilder.Length > 0 && urlBuilder[0] != '/')
                    {
                        urlBuilder.Insert(0, '/');
                    }
                    continue;
                }

                if (token.Type == PatternTokenType.Literal)
                {
                    urlBuilder.Insert(0, token.Name);
                    continue;
                }

                var tokenValue = userValues[token.Name];

                if (tokenValue != null)
                {
                    urlBuilder.Insert(0, tokenValue.ToString());
                }
            }

            urlBuilder.Insert(0, '/');
            return(urlBuilder.ToString());
        }
Example #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);
        }