/// <summary> /// Selects the best action given the provided route context and list of candidate actions. /// </summary> /// <param name="context">The current <see cref="RouteContext">route context</see> to evaluate.</param> /// <param name="candidates">The <see cref="IReadOnlyList{T}">read-only list</see> of candidate <see cref="ActionDescriptor">actions</see> to select from.</param> /// <returns>The best candidate <see cref="ActionDescriptor">action</see> or <c>null</c> if no candidate matches.</returns> public virtual ActionDescriptor SelectBestCandidate(RouteContext context, IReadOnlyList <ActionDescriptor> candidates) { Arg.NotNull(context, nameof(context)); Arg.NotNull(candidates, nameof(candidates)); var httpContext = context.HttpContext; if ((context.Handler = VerifyRequestedApiVersionIsNotAmbiguous(httpContext, out var apiVersion)) != null) { return(null); } var matches = EvaluateActionConstraints(context, candidates); var selectionContext = new ActionSelectionContext(httpContext, matches, apiVersion); var finalMatches = SelectBestActions(selectionContext); var properties = httpContext.ApiVersionProperties(); var selectionResult = properties.SelectionResult; properties.ApiVersion = selectionContext.RequestedVersion; selectionResult.AddCandidates(candidates); if (finalMatches == null) { selectionResult.EndIteration(); return(null); } if (finalMatches.Count == 1) { var selectedAction = finalMatches[0]; // note: short-circuit if the api version policy has already been applied to the match // and there are no other matches in a previous iteration which would take precendence if (selectedAction.VersionPolicyIsApplied() && !selectionResult.HasMatchesInPreviousIterations) { httpContext.ApiVersionProperties().ApiVersion = selectionContext.RequestedVersion; return(selectedAction); } } if (finalMatches.Count > 0) { var routeData = new RouteData(context.RouteData); var matchingActions = new MatchingActionSequence(finalMatches, routeData); selectionResult.AddMatches(matchingActions); selectionResult.TrySetBestMatch(matchingActions.BestMatch); } // note: even though we may have had a successful match, this method could be called multiple times. the final decision // is made by the IApiVersionRoutePolicy. we return here to make sure all candidates have been considered at least once. selectionResult.EndIteration(); return(null); }
void AppendPossibleMatches(IReadOnlyList <ActionDescriptor> matches, RouteContext context, ActionSelectionResult result) { Contract.Requires(matches != null); Contract.Requires(context != null); Contract.Requires(result != null); if (matches.Count == 0) { return; } var routeData = new RouteData(context.RouteData); var matchingActions = new MatchingActionSequence(matches, routeData); result.AddMatches(matchingActions); result.TrySetBestMatch(matchingActions.BestMatch); }