public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                ref var candidate = ref candidates[i];
                var     endpoint  = candidate.Endpoint;

                var page = endpoint.Metadata.GetMetadata <PageActionDescriptor>();
                if (page != null)
                {
                    if (page.CompiledPageDescriptor != null)
                    {
                        candidates.ReplaceEndpoint(i, page.CompiledPageDescriptor.Endpoint, candidate.Values);
                        continue;
                    }

                    // We found an endpoint instance that has a PageActionDescriptor, but not a
                    // CompiledPageActionDescriptor. Update the CandidateSet.
                    Task <CompiledPageActionDescriptor> compiled;
                    if (_loader is DefaultPageLoader defaultPageLoader)
                    {
                        compiled = defaultPageLoader.LoadAsync(page, endpoint.Metadata);
                    }
                    else
                    {
                        compiled = _loader.LoadAsync(page);
                    }

                    if (compiled.IsCompletedSuccessfully)
                    {
                        page.CompiledPageDescriptor = compiled.Result;
                        candidates.ReplaceEndpoint(i, compiled.Result.Endpoint, candidate.Values);
                    }
                    else
                    {
                        // In the most common case, GetOrAddAsync will return a synchronous result.
                        // Avoid going async since this is a fairly hot path.
                        return(ApplyAsyncAwaited(page, candidates, compiled, i));
                    }
                }
            }
Example #2
0
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            // There's no real benefit here from trying to avoid the async state machine.
            // We only execute on nodes that contain a dynamic policy, and thus always have
            // to await something.
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var endpoint = candidates[i].Endpoint;

                var metadata = endpoint.Metadata.GetMetadata <DynamicControllerMetadata>();
                if (metadata == null)
                {
                    continue;
                }

                var matchedValues = candidates[i].Values;
                var endpoints     = _selector.SelectEndpoints(metadata.Values);
                if (endpoints.Count == 0)
                {
                    // If there's no match this is a configuration error. We can't really check
                    // during startup that the action you configured exists.
                    throw new InvalidOperationException(
                              "Cannot find the fallback endpoint specified by route values: " +
                              "{ " + string.Join(", ", metadata.Values.Select(kvp => $"{kvp.Key}: {kvp.Value}")) + " }.");
                }

                // We need to provide the route values associated with this endpoint, so that features
                // like URL generation work.
                var values = new RouteValueDictionary(metadata.Values);

                // Include values that were matched by the fallback route.
                foreach (var kvp in matchedValues)
                {
                    values.TryAdd(kvp.Key, kvp.Value);
                }

                // Update the route values
                candidates.ReplaceEndpoint(i, endpoint, values);

                // Expand the list of endpoints
                candidates.ExpandEndpoint(i, endpoints, _comparer);
            }

            return(Task.CompletedTask);
        }
Example #3
0
        public async Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            // There's no real benefit here from trying to avoid the async state machine.
            // We only execute on nodes that contain a dynamic policy, and thus always have
            // to await something.
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var endpoint = candidates[i].Endpoint;

                var metadata = endpoint.Metadata.GetMetadata <DynamicPageMetadata>();
                if (metadata == null)
                {
                    continue;
                }

                var matchedValues = candidates[i].Values;
                var endpoints     = _selector.SelectEndpoints(metadata.Values);
                if (endpoints.Count == 0)
                {
                    // If there's no match this is a configuration error. We can't really check
                    // during startup that the action you configured exists.
                    throw new InvalidOperationException(
                              "Cannot find the fallback endpoint specified by route values: " +
                              "{ " + string.Join(", ", metadata.Values.Select(kvp => $"{kvp.Key}: {kvp.Value}")) + " }.");
                }

                // It is possible to have more than one result for pages but they are equivalent.

                var compiled = await _loader.LoadAsync(endpoints[0].Metadata.GetMetadata <PageActionDescriptor>());

                var replacement = compiled.Endpoint;

                // We need to provide the route values associated with this endpoint, so that features
                // like URL generation work.
                var values = new RouteValueDictionary(metadata.Values);

                // Include values that were matched by the fallback route.
                foreach (var kvp in matchedValues)
                {
                    values.TryAdd(kvp.Key, kvp.Value);
                }

                candidates.ReplaceEndpoint(i, replacement, values);
            }
        }
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate = ref candidates[i];
                var     endpoint  = (RouteEndpoint)candidate.Endpoint;

                var page = endpoint.Metadata.GetMetadata <PageActionDescriptor>();
                if (page != null)
                {
                    var compiled = _loader.Load(page);
                    candidates.ReplaceEndpoint(i, compiled.Endpoint, candidate.Values);
                }
            }
Example #5
0
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate   = ref candidates[i];
                var     newEndpoint = _negotiateEndpointCache.GetOrAdd(candidate.Endpoint, CreateNegotiateEndpoint);

                candidates.ReplaceEndpoint(i, newEndpoint, candidate.Values);
            }
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var metadata = candidates[i].Endpoint.Metadata.GetMetadata <Metadata>();
                if (metadata is null)
                {
                    continue;
                }

                // this one is OURS!
                //
                // since we own it we can update values in place.
                var values = candidates[i].Values ?? new RouteValueDictionary();

                if (!cache.TryGetValue(metadata, out var result))
                {
                    // use whatever criteria you want to match this to an MVC action. We're using an expression/type.
                    result = FindMatchingEndpoint(metadata, dataSource.Endpoints);

                    cache.TryAdd(metadata, result);
                }

                // emplace the MVC standard route values to be convincing
                var action = result.Metadata.GetMetadata <ControllerActionDescriptor>();
                foreach (var kvp in action.RouteValues)
                {
                    if (kvp.Value is string s && s.Length > 0)
                    {
                        values[kvp.Key] = kvp.Value;
                    }
                }

                if (result is null)
                {
                    throw new InvalidOperationException("Derp.");
                }

                candidates.ReplaceEndpoint(i, result, values);
            }

            return(Task.CompletedTask);
        }
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate = ref candidates[i];
                // Only apply to RouteEndpoint
                if (candidate.Endpoint is RouteEndpoint routeEndpoint)
                {
                    var hubMetadata = routeEndpoint.Metadata.GetMetadata <HubMetadata>();
                    // skip endpoint not apply hub.
                    if (hubMetadata != null)
                    {
                        var newEndpoint = _negotiateEndpointCache.GetOrAdd(hubMetadata.HubType, CreateNegotiateEndpoint(hubMetadata.HubType, routeEndpoint));

                        candidates.ReplaceEndpoint(i, newEndpoint, candidate.Values);
                    }
                }
            }
Example #8
0
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            for (var i = 0; i < candidates.Count; i++)
            {
                // 'PageLoaderMatcherPolicy' doesn't check if an endpoint is a valid candidate.
                // That's why we use this custom implementation that does it as other policies.
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                ref var candidate = ref candidates[i];
                var     endpoint  = candidate.Endpoint;

                var page = endpoint.Metadata.GetMetadata <PageActionDescriptor>();
                if (page != null)
                {
                    // We found an endpoint instance that has a PageActionDescriptor, but not a
                    // CompiledPageActionDescriptor. Update the CandidateSet.
                    var compiled = _loader.LoadAsync(page);
                    if (compiled.IsCompletedSuccessfully)
                    {
                        candidates.ReplaceEndpoint(i, compiled.Result.Endpoint, candidate.Values);
                    }
                    else
                    {
                        // In the most common case, GetOrAddAsync will return a synchronous result.
                        // Avoid going async since this is a fairly hot path.
                        return(ApplyAsyncAwaited(candidates, compiled, i));
                    }
                }
            }
Example #9
0
        public async Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            // There's no real benefit here from trying to avoid the async state machine.
            // We only execute on nodes that contain a dynamic policy, and thus always have
            // to await something.
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var endpoint       = candidates[i].Endpoint;
                var originalValues = candidates[i].Values;

                RouteValueDictionary dynamicValues = null;

                // We don't expect both of these to be provided, and they are internal so there's
                // no realistic way this could happen.
                var dynamicPageMetadata = endpoint.Metadata.GetMetadata <DynamicPageMetadata>();
                var transformerMetadata = endpoint.Metadata.GetMetadata <DynamicPageRouteValueTransformerMetadata>();
                if (dynamicPageMetadata != null)
                {
                    dynamicValues = dynamicPageMetadata.Values;
                }
                else if (transformerMetadata != null)
                {
                    var transformer = (DynamicRouteValueTransformer)httpContext.RequestServices.GetRequiredService(transformerMetadata.SelectorType);
                    dynamicValues = await transformer.TransformAsync(httpContext, originalValues);
                }
                else
                {
                    // Not a dynamic page
                    continue;
                }

                if (dynamicValues == null)
                {
                    candidates.ReplaceEndpoint(i, null, null);
                    continue;
                }

                var endpoints = _selector.SelectEndpoints(dynamicValues);
                if (endpoints.Count == 0 && dynamicPageMetadata != null)
                {
                    // Having no match for a fallback is a configuration error. We can't really check
                    // during startup that the action you configured exists, so this is the best we can do.
                    throw new InvalidOperationException(
                              "Cannot find the fallback endpoint specified by route values: " +
                              "{ " + string.Join(", ", dynamicValues.Select(kvp => $"{kvp.Key}: {kvp.Value}")) + " }.");
                }
                else if (endpoints.Count == 0)
                {
                    candidates.ReplaceEndpoint(i, null, null);
                    continue;
                }

                // We need to provide the route values associated with this endpoint, so that features
                // like URL generation work.
                var values = new RouteValueDictionary(dynamicValues);

                // Include values that were matched by the fallback route.
                if (originalValues != null)
                {
                    foreach (var kvp in originalValues)
                    {
                        values.TryAdd(kvp.Key, kvp.Value);
                    }
                }

                // Update the route values
                candidates.ReplaceEndpoint(i, endpoint, values);

                var loadedEndpoints = new List <Endpoint>(endpoints);
                for (var j = 0; j < loadedEndpoints.Count; j++)
                {
                    var compiled = await _loader.LoadAsync(loadedEndpoints[j].Metadata.GetMetadata <PageActionDescriptor>());

                    loadedEndpoints[j] = compiled.Endpoint;
                }

                // Expand the list of endpoints
                candidates.ExpandEndpoint(i, loadedEndpoints, _comparer);
            }
        }
Example #10
0
        public async Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            // The per-route selector, must be the same for all the endpoints we are dealing with.
            DynamicControllerEndpointSelector?selector = null;

            // There's no real benefit here from trying to avoid the async state machine.
            // We only execute on nodes that contain a dynamic policy, and thus always have
            // to await something.
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var endpoint       = candidates[i].Endpoint;
                var originalValues = candidates[i].Values !;

                RouteValueDictionary?dynamicValues = null;

                // We don't expect both of these to be provided, and they are internal so there's
                // no realistic way this could happen.
                var dynamicControllerMetadata = endpoint.Metadata.GetMetadata <DynamicControllerMetadata>();
                var transformerMetadata       = endpoint.Metadata.GetMetadata <DynamicControllerRouteValueTransformerMetadata>();

                DynamicRouteValueTransformer?transformer = null;
                if (dynamicControllerMetadata != null)
                {
                    dynamicValues = dynamicControllerMetadata.Values;
                }
                else if (transformerMetadata != null)
                {
                    transformer = (DynamicRouteValueTransformer)httpContext.RequestServices.GetRequiredService(transformerMetadata.SelectorType);
                    if (transformer.State != null)
                    {
                        throw new InvalidOperationException(Resources.FormatStateShouldBeNullForRouteValueTransformers(transformerMetadata.SelectorType.Name));
                    }
                    transformer.State = transformerMetadata.State;

                    dynamicValues = await transformer.TransformAsync(httpContext, originalValues);
                }
                else
                {
                    // Not a dynamic controller.
                    continue;
                }

                if (dynamicValues == null)
                {
                    candidates.ReplaceEndpoint(i, null, null);
                    continue;
                }

                selector = ResolveSelector(selector, endpoint);

                var endpoints = selector.SelectEndpoints(dynamicValues);
                if (endpoints.Count == 0 && dynamicControllerMetadata != null)
                {
                    // Naving no match for a fallback is a configuration error. We can't really check
                    // during startup that the action you configured exists, so this is the best we can do.
                    throw new InvalidOperationException(
                              "Cannot find the fallback endpoint specified by route values: " +
                              "{ " + string.Join(", ", dynamicValues.Select(kvp => $"{kvp.Key}: {kvp.Value}")) + " }.");
                }
                else if (endpoints.Count == 0)
                {
                    candidates.ReplaceEndpoint(i, null, null);
                    continue;
                }

                // We need to provide the route values associated with this endpoint, so that features
                // like URL generation work.
                var values = new RouteValueDictionary(dynamicValues);

                // Include values that were matched by the fallback route.
                if (originalValues != null)
                {
                    foreach (var kvp in originalValues)
                    {
                        values.TryAdd(kvp.Key, kvp.Value);
                    }
                }

                if (transformer != null)
                {
                    endpoints = await transformer.FilterAsync(httpContext, values, endpoints);

                    if (endpoints.Count == 0)
                    {
                        candidates.ReplaceEndpoint(i, null, null);
                        continue;
                    }
                }

                // Update the route values
                candidates.ReplaceEndpoint(i, endpoint, values);

                // Expand the list of endpoints
                candidates.ExpandEndpoint(i, endpoints, _comparer);
            }
        }
Example #11
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="candidates"></param>
        /// <returns></returns>
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            // The goal of this method is to perform the final matching:
            // Map between route values matched by the template and the ones we want to expose to the action for binding.
            // (tweaking the route values is fine here)
            // Invalidating the candidate if the key/function values are not valid/missing.
            // Perform overload resolution for functions by looking at the candidates and their metadata.
            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate = ref candidates[i];
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var oDataMetadata = candidate.Endpoint.Metadata.OfType <ODataEndpointMetadata>().FirstOrDefault();
                if (oDataMetadata == null)
                {
                    continue;
                }

                var original = candidate.Endpoint.RequestDelegate;
                var name     = candidate.Endpoint.DisplayName;

                var newEndpoint    = new Endpoint(EndpointWithODataPath, candidate.Endpoint.Metadata, name);
                var originalValues = candidate.Values;
                var newValues      = new RouteValueDictionary();
                foreach (var(key, value) in originalValues)
                {
                    //if (key.EndsWith(".Name"))
                    //{
                    //    var keyValue = originalValues[key.Replace(".Name", ".Value")];
                    //    var partName = originalValues[key];
                    //    var parameterName = oDataMetadata.ParameterMappings[oDataMetadata.ParameterMappings.Keys.Single(key => key.Name == (string)partName)];
                    //    newValues.Add(parameterName, keyValue);
                    //}

                    newValues.Add(key, value);
                }

                candidates.ReplaceEndpoint(i, newEndpoint, newValues);

                Task EndpointWithODataPath(HttpContext httpContext)
                {
                    var odataPath = oDataMetadata.ODataPathFactory(httpContext.GetRouteData().Values, oDataMetadata.ParameterMappings);
                    var odata     = httpContext.Request.ODataFeature();

                    odata.IsEndpointRouting = true;
                    odata.RequestContainer  = httpContext.RequestServices;
                    odata.Path      = odataPath;
                    odata.RouteName = name;
                    var prc = httpContext.RequestServices.GetRequiredService <IPerRouteContainer>();

                    if (!prc.HasODataRootContainer(name))
                    {
                        prc.AddRoute(odata.RouteName, "");
                    }

                    return(original(httpContext));
                }
            }