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