/// <summary> /// Evaluates whether a match is better than the current match and if so returns the replacement; otherwise returns the /// current match. /// </summary> protected virtual MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement) { if (potentialReplacement == null) { return current; } if (current != null) { return (potentialReplacement.Quality > current.Quality) ? potentialReplacement : current; } return potentialReplacement; }
/// <summary> /// Select the best match among the candidate matches found. /// </summary> /// <param name="matches">The collection of matches.</param> /// <returns>The <see cref="MediaTypeFormatterMatch"/> determined to be the best match.</returns> protected virtual MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection <MediaTypeFormatterMatch> matches) { // Performance-sensitive if (matches == null) { throw Error.ArgumentNull("matches"); } List <MediaTypeFormatterMatch> matchList = matches.AsList(); MediaTypeFormatterMatch bestMatchOnType = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderLiteral = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderSubtypeMediaRange = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderAllMediaRange = null; MediaTypeFormatterMatch bestMatchOnMediaTypeMapping = null; MediaTypeFormatterMatch bestMatchOnRequestMediaType = null; // Go through each formatter to find the best match in each category. for (int i = 0; i < matchList.Count; i++) { MediaTypeFormatterMatch match = matchList[i]; switch (match.Ranking) { case MediaTypeFormatterMatchRanking.MatchOnCanWriteType: // First match by type trumps all other type matches if (bestMatchOnType == null) { bestMatchOnType = match; } break; case MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping: // Matches on accept headers using mappings must choose the highest quality match bestMatchOnMediaTypeMapping = this.UpdateBestMatch(bestMatchOnMediaTypeMapping, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderLiteral = this.UpdateBestMatch(bestMatchOnAcceptHeaderLiteral, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderSubtypeMediaRange = this.UpdateBestMatch(bestMatchOnAcceptHeaderSubtypeMediaRange, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderAllMediaRange = this.UpdateBestMatch(bestMatchOnAcceptHeaderAllMediaRange, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestMediaType: // First match on request content type trumps other request content matches if (bestMatchOnRequestMediaType == null) { bestMatchOnRequestMediaType = match; } break; } } // If we received matches based on both supported media types and from media type mappings, // we want to give precedence to the media type mappings, but only if their quality is >= that of the supported media type. // We do this because media type mappings are the user's extensibility point and must take precedence over normal // supported media types in the case of a tie. The 99% case is where both have quality 1.0. if (bestMatchOnMediaTypeMapping != null) { MediaTypeFormatterMatch mappingOverride = bestMatchOnMediaTypeMapping; mappingOverride = this.UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderLiteral); mappingOverride = this.UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderSubtypeMediaRange); mappingOverride = this.UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderAllMediaRange); if (mappingOverride != bestMatchOnMediaTypeMapping) { bestMatchOnMediaTypeMapping = null; } } // now select the formatter and media type // A MediaTypeMapping is highest precedence -- it is an extensibility point // allowing the user to override normal accept header matching MediaTypeFormatterMatch bestMatch = null; if (bestMatchOnMediaTypeMapping != null) { bestMatch = bestMatchOnMediaTypeMapping; } else if (bestMatchOnAcceptHeaderLiteral != null || bestMatchOnAcceptHeaderSubtypeMediaRange != null || bestMatchOnAcceptHeaderAllMediaRange != null) { bestMatch = this.UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderLiteral); bestMatch = this.UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderSubtypeMediaRange); bestMatch = this.UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderAllMediaRange); } else if (bestMatchOnRequestMediaType != null) { bestMatch = bestMatchOnRequestMediaType; } else if (bestMatchOnType != null) { bestMatch = bestMatchOnType; } return(bestMatch); }
/// <summary> /// Evaluates whether a match is better than the current match and if so returns the replacement; otherwise returns the /// current match. /// </summary> protected virtual MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement) { if (potentialReplacement == null) { return(current); } if (current != null) { return((potentialReplacement.Quality > current.Quality) ? potentialReplacement : current); } return(potentialReplacement); }
/// <summary> /// Determine how well each formatter matches by associating a <see cref="MediaTypeFormatterMatchRanking"/> value /// with the formatter. Then associate the quality of the match based on q-factors and other parameters. The result of this /// method is a collection of the matches found categorized and assigned a quality value. /// </summary> /// <param name="type">The type to be serialized.</param> /// <param name="request">The request.</param> /// <param name="formatters">The set of <see cref="MediaTypeFormatter"/> objects from which to choose.</param> /// <returns>A collection containing all the matches.</returns> protected virtual Collection <MediaTypeFormatterMatch> ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable <MediaTypeFormatter> formatters) { // Performance-sensitive if (type == null) { throw Error.ArgumentNull("type"); } if (request == null) { throw Error.ArgumentNull("request"); } if (formatters == null) { throw Error.ArgumentNull("formatters"); } IEnumerable <MediaTypeWithQualityHeaderValue> sortedAcceptValues = null; // Go through each formatter to find how well it matches. ListWrapperCollection <MediaTypeFormatterMatch> matches = new ListWrapperCollection <MediaTypeFormatterMatch>(); MediaTypeFormatter[] writingFormatters = GetWritingFormatters(formatters); for (int i = 0; i < writingFormatters.Length; i++) { MediaTypeFormatter formatter = writingFormatters[i]; MediaTypeFormatterMatch match = null; // Check first that formatter can write the actual type if (!formatter.CanWriteType(type)) { // Formatter can't even write the type so no match at all continue; } // Match against media type mapping. if ((match = this.MatchMediaTypeMapping(request, formatter)) != null) { matches.Add(match); continue; } // Match against the accept header values. if (sortedAcceptValues == null) { // Sort the Accept header values in descending order based on q-factor sortedAcceptValues = this.SortMediaTypeWithQualityHeaderValuesByQFactor(request.Headers.Accept); } if ((match = this.MatchAcceptHeader(sortedAcceptValues, formatter)) != null) { matches.Add(match); continue; } // Match against request's media type if any if ((match = this.MatchRequestMediaType(request, formatter)) != null) { matches.Add(match); continue; } // Check whether we should match on type or stop the matching process. // The latter is used to generate 406 (Not Acceptable) status codes. bool shouldMatchOnType = this.ShouldMatchOnType(sortedAcceptValues); // Match against the type of object we are writing out if (shouldMatchOnType && (match = this.MatchType(type, formatter)) != null) { matches.Add(match); continue; } } return(matches); }