public void MatchUrlGeneration_DoesNotLogData()
    {
        // Arrange
        var sink   = new TestSink();
        var logger = new TestLogger(_name, sink, enabled: true);

        var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
        var constraints          = new Dictionary <string, IRouteConstraint>
        {
            { "a", new PassConstraint() },
            { "b", new FailConstraint() }
        };

        // Act
        RouteConstraintMatcher.Match(
            constraints: constraints,
            routeValues: routeValueDictionary,
            httpContext: new Mock <HttpContext>().Object,
            route: new Mock <IRouter>().Object,
            routeDirection: RouteDirection.UrlGeneration,
            logger: logger);

        // Assert
        // There are no BeginScopes called.
        Assert.Empty(sink.Scopes);

        // There are no WriteCores called.
        Assert.Empty(sink.Writes);
    }
Exemple #2
0
        /// <inheritdoc />
        public async Task RouteAsync(RouteContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            foreach (var matchingEntry in _matchingEntries)
            {
                var requestPath = context.HttpContext.Request.Path;
                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.LogVerbose(
                    "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;
                }
            }
        }
        /// <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;
                        if (!matcher.TryMatch(context.HttpContext.Request.Path, context.RouteData.Values))
                        {
                            continue;
                        }

                        try
                        {
                            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();
                            }
                        }
                    }
                }
            }
        }
Exemple #4
0
        /// <inheritdoc />
        public async Task RouteAsync(RouteContext context)
        {
            foreach (var tree in _trees)
            {
                var tokenizer  = new PathTokenizer(context.HttpContext.Request.Path);
                var enumerator = tokenizer.GetEnumerator();
                var root       = tree.Root;

                var treeEnumerator = new TreeEnumerator(root, tokenizer);

                while (treeEnumerator.MoveNext())
                {
                    var node = treeEnumerator.Current;
                    foreach (var item in node.Matches)
                    {
                        var values = item.TemplateMatcher.Match(context.HttpContext.Request.Path);
                        if (values == null)
                        {
                            continue;
                        }

                        var match    = new TemplateMatch(item, values);
                        var snapshot = context.RouteData.PushState(match.Entry.Target, match.Values, dataTokens: null);

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

                            _logger.MatchedRoute(match.Entry.RouteName, match.Entry.RouteTemplate.TemplateText);

                            await match.Entry.Target.RouteAsync(context);

                            if (context.Handler != null)
                            {
                                return;
                            }
                        }
                        finally
                        {
                            if (context.Handler == null)
                            {
                                // Restore the original values to prevent polluting the route data.
                                snapshot.Restore();
                            }
                        }
                    }
                }
            }
        }
Exemple #5
0
        public async virtual Task RouteAsync([NotNull] RouteContext context)
        {
            EnsureLoggers(context.HttpContext);

            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 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))
            {
                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;
                }
            }
        }
Exemple #6
0
    /// <inheritdoc />
    public virtual VirtualPathData?GetVirtualPath(VirtualPathContext context)
    {
        EnsureBinder(context.HttpContext);
        EnsureLoggers(context.HttpContext);

        var values = _binder.GetValues(context.AmbientValues, context.Values);

        if (values == null)
        {
            // We're missing one of the required values for this route.
            return(null);
        }

        if (!RouteConstraintMatcher.Match(
                Constraints,
                values.CombinedValues,
                context.HttpContext,
                this,
                RouteDirection.UrlGeneration,
                _constraintLogger))
        {
            return(null);
        }

        context.Values = values.CombinedValues;

        var pathData = OnVirtualPathGenerated(context);

        if (pathData != null)
        {
            // If the target generates a value then that can short circuit.
            return(pathData);
        }

        // If we can produce a value go ahead and do it, the caller can check context.IsBound
        // to see if the values were validated.

        // When we still cannot produce a value, this should return null.
        var virtualPath = _binder.BindValues(values.AcceptedValues);

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

        pathData = new VirtualPathData(this, virtualPath);
        if (DataTokens != null)
        {
            foreach (var dataToken in DataTokens)
            {
                pathData.DataTokens.Add(dataToken.Key, dataToken.Value);
            }
        }

        return(pathData);
    }
 public void ReturnsTrueOnNullInput()
 {
     Assert.True(RouteConstraintMatcher.Match(
                     constraints: null,
                     routeValues: new RouteValueDictionary(),
                     httpContext: new Mock <HttpContext>().Object,
                     route: new Mock <IRouter>().Object,
                     routeDirection: RouteDirection.IncomingRequest,
                     logger: NullLogger.Instance));
 }
Exemple #8
0
        public virtual string GetVirtualPath(VirtualPathContext context)
        {
            var values = _binder.GetValues(context.AmbientValues, context.Values);

            if (values == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            EnsureLoggers(context.Context);
            if (!RouteConstraintMatcher.Match(Constraints,
                                              values.CombinedValues,
                                              context.Context,
                                              this,
                                              RouteDirection.UrlGeneration,
                                              _constraintLogger))
            {
                return(null);
            }

            // Validate that the target can accept these values.
            var childContext = CreateChildVirtualPathContext(context, values.AcceptedValues);
            var path         = _target.GetVirtualPath(childContext);

            if (path != null)
            {
                // If the target generates a value then that can short circuit.
                context.IsBound = true;
                return(path);
            }
            else if (!childContext.IsBound)
            {
                // The target has rejected these values.
                return(null);
            }

            path = _binder.BindValues(values.AcceptedValues);
            if (path != null)
            {
                context.IsBound = true;
            }

            return(path);
        }
    public void ReturnsTrueOnValidConstraints()
    {
        var constraints = new Dictionary <string, IRouteConstraint>
        {
            { "a", new PassConstraint() },
            { "b", new PassConstraint() }
        };

        var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });

        Assert.True(RouteConstraintMatcher.Match(
                        constraints: constraints,
                        routeValues: routeValueDictionary,
                        httpContext: new Mock <HttpContext>().Object,
                        route: new Mock <IRouter>().Object,
                        routeDirection: RouteDirection.IncomingRequest,
                        logger: NullLogger.Instance));
    }
    private TestSink SetUpMatch(Dictionary <string, IRouteConstraint> constraints, bool loggerEnabled)
    {
        // Arrange
        var sink   = new TestSink();
        var logger = new TestLogger(_name, sink, loggerEnabled);

        var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });

        // Act
        RouteConstraintMatcher.Match(
            constraints: constraints,
            routeValues: routeValueDictionary,
            httpContext: new Mock <HttpContext>().Object,
            route: new Mock <IRouter>().Object,
            routeDirection: RouteDirection.IncomingRequest,
            logger: logger);
        return(sink);
    }
Exemple #11
0
    /// <inheritdoc />
    public virtual Task RouteAsync(RouteContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        EnsureMatcher();
        EnsureLoggers(context.HttpContext);

        var requestPath = context.HttpContext.Request.Path;

        if (!_matcher.TryMatch(requestPath, context.RouteData.Values))
        {
            // If we got back a null value set, that means the URI did not match
            return(Task.CompletedTask);
        }

        // Perf: Avoid accessing dictionaries if you don't need to write to them, these dictionaries are all
        // created lazily.
        if (DataTokens.Count > 0)
        {
            MergeValues(context.RouteData.DataTokens, DataTokens);
        }

        if (!RouteConstraintMatcher.Match(
                Constraints,
                context.RouteData.Values,
                context.HttpContext,
                this,
                RouteDirection.IncomingRequest,
                _constraintLogger))
        {
            return(Task.CompletedTask);
        }
        Log.RequestMatchedRoute(_logger, Name, ParsedTemplate.TemplateText);

        return(OnRouteMatched(context));
    }
Exemple #12
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));
        }
Exemple #13
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));
                        }
                    }
                }
            }
        }
        /// <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;
                }
            }

            if (!context.IsHandled)
            {
                _logger.LogVerbose("Request did not match any attribute route.");
            }
        }
Exemple #15
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;
                }
            }
        }
        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));
        }
        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;
                    }
                }
            }
        }