private VirtualPathData GenerateVirtualPath(
            VirtualPathContext context,
            OutboundRouteEntry entry,
            TemplateBinder binder)
        {
            // In attribute the context includes the values that are used to select this entry - typically
            // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't
            // want to pass these to the link generation code, or else they will end up as query parameters.
            //
            // So, we need to exclude from here any values that are 'required link values', but aren't
            // parameters in the template.
            //
            // Ex:
            //      template: api/Products/{action}
            //      required values: { id = "5", action = "Buy", Controller = "CoolProducts" }
            //
            //      result: { id = "5", action = "Buy" }
            var inputValues = new RouteValueDictionary();

            foreach (var kvp in context.Values)
            {
                if (entry.RequiredLinkValues.ContainsKey(kvp.Key))
                {
                    var parameter = entry.RouteTemplate.GetParameter(kvp.Key);

                    if (parameter == null)
                    {
                        continue;
                    }
                }

                inputValues.Add(kvp.Key, kvp.Value);
            }

            var bindingResult = binder.GetValues(context.AmbientValues, inputValues);

            if (bindingResult == null)
            {
                // A required parameter in the template didn't get a value.
                return(null);
            }

            var matched = RouteConstraintMatcher.Match(
                entry.Constraints,
                bindingResult.CombinedValues,
                context.HttpContext,
                this,
                RouteDirection.UrlGeneration,
                _constraintLogger);

            if (!matched)
            {
                // A constraint rejected this link.
                return(null);
            }

            var pathData = entry.Handler.GetVirtualPath(context);

            if (pathData != null)
            {
                // If path is non-null then the target router short-circuited, we don't expect this
                // in typical MVC scenarios.
                return(pathData);
            }

            var path = binder.BindValues(bindingResult.AcceptedValues);

            if (path == null)
            {
                return(null);
            }

            return(new VirtualPathData(this, path));
        }
Exemplo n.º 2
0
        private VirtualPathData GenerateVirtualPath(VirtualPathContext context, AttributeRouteLinkGenerationEntry entry)
        {
            // In attribute the context includes the values that are used to select this entry - typically
            // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't
            // want to pass these to the link generation code, or else they will end up as query parameters.
            //
            // So, we need to exclude from here any values that are 'required link values', but aren't
            // parameters in the template.
            //
            // Ex:
            //      template: api/Products/{action}
            //      required values: { id = "5", action = "Buy", Controller = "CoolProducts" }
            //
            //      result: { id = "5", action = "Buy" }
            var inputValues = new Dictionary <string, object>(StringComparer.OrdinalIgnoreCase);

            foreach (var kvp in context.Values)
            {
                if (entry.RequiredLinkValues.ContainsKey(kvp.Key))
                {
                    var parameter = entry.Template.Parameters
                                    .FirstOrDefault(p => string.Equals(p.Name, kvp.Key, StringComparison.OrdinalIgnoreCase));

                    if (parameter == null)
                    {
                        continue;
                    }
                }

                inputValues.Add(kvp.Key, kvp.Value);
            }

            var bindingResult = entry.Binder.GetValues(context.AmbientValues, inputValues);

            if (bindingResult == null)
            {
                // A required parameter in the template didn't get a value.
                return(null);
            }

            var matched = RouteConstraintMatcher.Match(
                entry.Constraints,
                bindingResult.CombinedValues,
                context.Context,
                this,
                RouteDirection.UrlGeneration,
                _constraintLogger);

            if (!matched)
            {
                // A constraint rejected this link.
                return(null);
            }

            // These values are used to signal to the next route what we would produce if we round-tripped
            // (generate a link and then parse). In MVC the 'next route' is typically the MvcRouteHandler.
            var providedValues = new Dictionary <string, object>(
                bindingResult.AcceptedValues,
                StringComparer.OrdinalIgnoreCase);

            providedValues.Add(AttributeRouting.RouteGroupKey, entry.RouteGroup);

            var childContext = new VirtualPathContext(context.Context, context.AmbientValues, context.Values)
            {
                ProvidedValues = providedValues,
            };

            var pathData = _next.GetVirtualPath(childContext);

            if (pathData != null)
            {
                // If path is non-null then the target router short-circuited, we don't expect this
                // in typical MVC scenarios.
                return(pathData);
            }
            else if (!childContext.IsBound)
            {
                // The target router has rejected these values. We don't expect this in typical MVC scenarios.
                return(null);
            }

            var path = entry.Binder.BindValues(bindingResult.AcceptedValues);

            if (path == null)
            {
                return(null);
            }

            return(new VirtualPathData(this, path));
        }
        /// <inheritdoc />
        public async Task RouteAsync(RouteContext context)
        {
            foreach (var tree in _trees)
            {
                var tokenizer = new PathTokenizer(context.HttpContext.Request.Path);
                var root      = tree.Root;

                var treeEnumerator = new TreeEnumerator(root, tokenizer);

                // Create a snapshot before processing the route. We'll restore this snapshot before running each
                // to restore the state. This is likely an "empty" snapshot, which doesn't allocate.
                var snapshot = context.RouteData.PushState(router: null, values: null, dataTokens: null);

                while (treeEnumerator.MoveNext())
                {
                    var node = treeEnumerator.Current;
                    foreach (var item in node.Matches)
                    {
                        var entry   = item.Entry;
                        var matcher = item.TemplateMatcher;

                        try
                        {
                            if (!matcher.TryMatch(context.HttpContext.Request.Path, context.RouteData.Values))
                            {
                                continue;
                            }

                            if (!RouteConstraintMatcher.Match(
                                    entry.Constraints,
                                    context.RouteData.Values,
                                    context.HttpContext,
                                    this,
                                    RouteDirection.IncomingRequest,
                                    _constraintLogger))
                            {
                                continue;
                            }

                            _logger.MatchedRoute(entry.RouteName, entry.RouteTemplate.TemplateText);
                            context.RouteData.Routers.Add(entry.Handler);

                            await entry.Handler.RouteAsync(context);

                            if (context.Handler != null)
                            {
                                return;
                            }
                        }
                        finally
                        {
                            if (context.Handler == null)
                            {
                                // Restore the original values to prevent polluting the route data.
                                snapshot.Restore();
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 4
0
        /// <inheritdoc />
        public async Task RouteAsync([NotNull] RouteContext context)
        {
            foreach (var matchingEntry in _matchingEntries)
            {
                var requestPath = context.HttpContext.Request.Path.Value;

                if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
                {
                    requestPath = requestPath.Substring(1);
                }

                var values = matchingEntry.TemplateMatcher.Match(requestPath);
                if (values == null)
                {
                    // If we got back a null value set, that means the URI did not match
                    continue;
                }

                var oldRouteData = context.RouteData;

                var newRouteData = new RouteData(oldRouteData);
                newRouteData.Routers.Add(matchingEntry.Target);
                MergeValues(newRouteData.Values, values);

                if (!RouteConstraintMatcher.Match(
                        matchingEntry.Constraints,
                        newRouteData.Values,
                        context.HttpContext,
                        this,
                        RouteDirection.IncomingRequest,
                        _constraintLogger))
                {
                    continue;
                }

                _logger.LogInformation(
                    "Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.",
                    matchingEntry.RouteName,
                    matchingEntry.RouteTemplate);

                try
                {
                    context.RouteData = newRouteData;

                    await matchingEntry.Target.RouteAsync(context);
                }
                finally
                {
                    // Restore the original values to prevent polluting the route data.
                    if (!context.IsHandled)
                    {
                        context.RouteData = oldRouteData;
                    }
                }

                if (context.IsHandled)
                {
                    break;
                }
            }
        }
Exemplo n.º 5
0
        public async virtual Task RouteAsync([NotNull] RouteContext context)
        {
            EnsureLoggers(context.HttpContext);
            using (_logger.BeginScope("TemplateRoute.RouteAsync"))
            {
                var requestPath = context.HttpContext.Request.Path.Value;

                if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
                {
                    requestPath = requestPath.Substring(1);
                }

                var values = _matcher.Match(requestPath, Defaults);

                if (values == null)
                {
                    if (_logger.IsEnabled(TraceType.Verbose))
                    {
                        _logger.WriteValues(CreateRouteAsyncValues(
                                                requestPath,
                                                values,
                                                matchedValues: false,
                                                matchedConstraints: false,
                                                handled: context.IsHandled));
                    }

                    // If we got back a null value set, that means the URI did not match
                    return;
                }
                else
                {
                    // Not currently doing anything to clean this up if it's not a match. Consider hardening this.
                    context.RouteData.Values = values;

                    if (RouteConstraintMatcher.Match(Constraints,
                                                     values,
                                                     context.HttpContext,
                                                     this,
                                                     RouteDirection.IncomingRequest,
                                                     _constraintLogger))
                    {
                        context.RouteData.DataTokens = _dataTokens;
                        await _target.RouteAsync(context);

                        if (_logger.IsEnabled(TraceType.Verbose))
                        {
                            _logger.WriteValues(CreateRouteAsyncValues(
                                                    requestPath,
                                                    values,
                                                    matchedValues: true,
                                                    matchedConstraints: true,
                                                    handled: context.IsHandled));
                        }
                    }
                    else
                    {
                        if (_logger.IsEnabled(TraceType.Verbose))
                        {
                            _logger.WriteValues(CreateRouteAsyncValues(
                                                    requestPath,
                                                    values,
                                                    matchedValues: true,
                                                    matchedConstraints: false,
                                                    handled: context.IsHandled));
                        }
                    }
                }
            }
        }
Exemplo n.º 6
0
        public async virtual Task RouteAsync(RouteContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            EnsureLoggers(context.HttpContext);

            var requestPath = context.HttpContext.Request.Path;
            var values      = _matcher.Match(requestPath);

            if (values == null)
            {
                // If we got back a null value set, that means the URI did not match
                return;
            }

            var oldRouteData = context.RouteData;

            var newRouteData = new RouteData(oldRouteData);

            // Perf: Avoid accessing data tokens if you don't need to write to it, these dictionaries are all
            // created lazily.
            if (_dataTokens.Count > 0)
            {
                MergeValues(newRouteData.DataTokens, _dataTokens);
            }

            newRouteData.Routers.Add(_target);
            MergeValues(newRouteData.Values, values);

            if (!RouteConstraintMatcher.Match(
                    Constraints,
                    newRouteData.Values,
                    context.HttpContext,
                    this,
                    RouteDirection.IncomingRequest,
                    _constraintLogger))
            {
                return;
            }

            _logger.LogVerbose(
                "Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.",
                Name,
                RouteTemplate);

            try
            {
                context.RouteData = newRouteData;

                await _target.RouteAsync(context);
            }
            finally
            {
                // Restore the original values to prevent polluting the route data.
                if (!context.IsHandled)
                {
                    context.RouteData = oldRouteData;
                }
            }
        }
        public async virtual Task RouteAsync([NotNull] RouteContext context)
        {
            EnsureLoggers(context.HttpContext);
            using (_logger.BeginScope("TemplateRoute.RouteAsync"))
            {
                var requestPath = context.HttpContext.Request.Path.Value;

                if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
                {
                    requestPath = requestPath.Substring(1);
                }

                var values = _matcher.Match(requestPath);

                if (values == null)
                {
                    if (_logger.IsEnabled(LogLevel.Verbose))
                    {
                        _logger.WriteValues(CreateRouteAsyncValues(
                                                requestPath,
                                                context.RouteData.Values,
                                                matchedValues: false,
                                                matchedConstraints: false,
                                                handled: context.IsHandled));
                    }

                    // If we got back a null value set, that means the URI did not match
                    return;
                }

                var oldRouteData = context.RouteData;

                var newRouteData = new RouteData(oldRouteData);
                MergeValues(newRouteData.DataTokens, _dataTokens);
                newRouteData.Routers.Add(_target);
                MergeValues(newRouteData.Values, values);

                if (!RouteConstraintMatcher.Match(
                        Constraints,
                        newRouteData.Values,
                        context.HttpContext,
                        this,
                        RouteDirection.IncomingRequest,
                        _constraintLogger))
                {
                    if (_logger.IsEnabled(LogLevel.Verbose))
                    {
                        _logger.WriteValues(CreateRouteAsyncValues(
                                                requestPath,
                                                newRouteData.Values,
                                                matchedValues: true,
                                                matchedConstraints: false,
                                                handled: context.IsHandled));
                    }

                    return;
                }

                try
                {
                    context.RouteData = newRouteData;

                    await _target.RouteAsync(context);

                    if (_logger.IsEnabled(LogLevel.Verbose))
                    {
                        _logger.WriteValues(CreateRouteAsyncValues(
                                                requestPath,
                                                newRouteData.Values,
                                                matchedValues: true,
                                                matchedConstraints: true,
                                                handled: context.IsHandled));
                    }
                }
                finally
                {
                    // Restore the original values to prevent polluting the route data.
                    if (!context.IsHandled)
                    {
                        context.RouteData = oldRouteData;
                    }
                }
            }
        }