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)); } } }
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); }
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); } }
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); } } }
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)); } } }
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); } }
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); } }
/// <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)); } }