diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/CollectionExtensions.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/CollectionExtensions.cs
new file mode 100644
index 0000000000..c8d80c0048
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/CollectionExtensions.cs
@@ -0,0 +1,275 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.ObjectModel;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace System.Collections.Generic
+{
+ ///
+ /// Helper extension methods for fast use of collections.
+ ///
+ internal static class CollectionExtensions
+ {
+ ///
+ /// Return a new array with the value added to the end. Slow and best suited to long lived arrays with few writes relative to reads.
+ ///
+ public static T[] AppendAndReallocate(this T[] array, T value)
+ {
+ Contract.Assert(array != null);
+
+ int originalLength = array.Length;
+ T[] newArray = new T[originalLength + 1];
+ array.CopyTo(newArray, 0);
+ newArray[originalLength] = value;
+ return newArray;
+ }
+
+ ///
+ /// Return the enumerable as an Array, copying if required. Optimized for common case where it is an Array.
+ /// Avoid mutating the return value.
+ ///
+ public static T[] AsArray(this IEnumerable values)
+ {
+ Contract.Assert(values != null);
+
+ T[] array = values as T[];
+ if (array == null)
+ {
+ array = values.ToArray();
+ }
+ return array;
+ }
+
+ ///
+ /// Return the enumerable as a Collection of T, copying if required. Optimized for the common case where it is
+ /// a Collection of T and avoiding a copy if it implements IList of T. Avoid mutating the return value.
+ ///
+ public static Collection AsCollection(this IEnumerable enumerable)
+ {
+ Contract.Assert(enumerable != null);
+
+ Collection collection = enumerable as Collection;
+ if (collection != null)
+ {
+ return collection;
+ }
+ // Check for IList so that collection can wrap it instead of copying
+ IList list = enumerable as IList;
+ if (list == null)
+ {
+ list = new List(enumerable);
+ }
+ return new Collection(list);
+ }
+
+ ///
+ /// Return the enumerable as a IList of T, copying if required. Avoid mutating the return value.
+ ///
+ public static IList AsIList(this IEnumerable enumerable)
+ {
+ Contract.Assert(enumerable != null);
+
+ IList list = enumerable as IList;
+ if (list != null)
+ {
+ return list;
+ }
+ return new List(enumerable);
+ }
+
+ ///
+ /// Return the enumerable as a List of T, copying if required. Optimized for common case where it is an List of T
+ /// or a ListWrapperCollection of T. Avoid mutating the return value.
+ ///
+ public static List AsList(this IEnumerable enumerable)
+ {
+ Contract.Assert(enumerable != null);
+
+ List list = enumerable as List;
+ if (list != null)
+ {
+ return list;
+ }
+ ListWrapperCollection listWrapper = enumerable as ListWrapperCollection;
+ if (listWrapper != null)
+ {
+ return listWrapper.ItemsList;
+ }
+ return new List(enumerable);
+ }
+
+ ///
+ /// Remove values from the list starting at the index start.
+ ///
+ public static void RemoveFrom(this List list, int start)
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(start >= 0 && start <= list.Count);
+
+ list.RemoveRange(start, list.Count - start);
+ }
+
+ ///
+ /// Return the only value from list, the type's default value if empty, or call the errorAction for 2 or more.
+ ///
+ public static T SingleDefaultOrError(this IList list, Action errorAction, TArg1 errorArg1)
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(errorAction != null);
+
+ switch (list.Count)
+ {
+ case 0:
+ return default(T);
+
+ case 1:
+ T value = list[0];
+ return value;
+
+ default:
+ errorAction(errorArg1);
+ return default(T);
+ }
+ }
+
+ ///
+ /// Returns a single value in list matching type TMatch if there is only one, null if there are none of type TMatch or calls the
+ /// errorAction with errorArg1 if there is more than one.
+ ///
+ public static TMatch SingleOfTypeDefaultOrError(this IList list, Action errorAction, TArg1 errorArg1) where TMatch : class
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(errorAction != null);
+
+ TMatch result = null;
+ for (int i = 0; i < list.Count; i++)
+ {
+ TMatch typedValue = list[i] as TMatch;
+ if (typedValue != null)
+ {
+ if (result == null)
+ {
+ result = typedValue;
+ }
+ else
+ {
+ errorAction(errorArg1);
+ return null;
+ }
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// Convert an ICollection to an array, removing null values. Fast path for case where there are no null values.
+ ///
+ public static T[] ToArrayWithoutNulls(this ICollection collection) where T : class
+ {
+ Contract.Assert(collection != null);
+
+ T[] result = new T[collection.Count];
+ int count = 0;
+ foreach (T value in collection)
+ {
+ if (value != null)
+ {
+ result[count] = value;
+ count++;
+ }
+ }
+ if (count == collection.Count)
+ {
+ return result;
+ }
+ else
+ {
+ T[] trimmedResult = new T[count];
+ Array.Copy(result, trimmedResult, count);
+ return trimmedResult;
+ }
+ }
+
+ ///
+ /// Convert the array to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for array input.
+ ///
+ public static Dictionary ToDictionaryFast(this TValue[] array, Func keySelector, IEqualityComparer comparer)
+ {
+ Contract.Assert(array != null);
+ Contract.Assert(keySelector != null);
+
+ Dictionary dictionary = new Dictionary(array.Length, comparer);
+ for (int i = 0; i < array.Length; i++)
+ {
+ TValue value = array[i];
+ dictionary.Add(keySelector(value), value);
+ }
+ return dictionary;
+ }
+
+ ///
+ /// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input with fast path for array.
+ ///
+ public static Dictionary ToDictionaryFast(this IList list, Func keySelector, IEqualityComparer comparer)
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(keySelector != null);
+
+ TValue[] array = list as TValue[];
+ if (array != null)
+ {
+ return ToDictionaryFast(array, keySelector, comparer);
+ }
+ return ToDictionaryFastNoCheck(list, keySelector, comparer);
+ }
+
+ ///
+ /// Convert the enumerable to a Dictionary using the keySelector to extract keys from values and the specified comparer. Fast paths for array and IList of T.
+ ///
+ public static Dictionary ToDictionaryFast(this IEnumerable enumerable, Func keySelector, IEqualityComparer comparer)
+ {
+ Contract.Assert(enumerable != null);
+ Contract.Assert(keySelector != null);
+
+ TValue[] array = enumerable as TValue[];
+ if (array != null)
+ {
+ return ToDictionaryFast(array, keySelector, comparer);
+ }
+ IList list = enumerable as IList;
+ if (list != null)
+ {
+ return ToDictionaryFastNoCheck(list, keySelector, comparer);
+ }
+ Dictionary dictionary = new Dictionary(comparer);
+ foreach (TValue value in enumerable)
+ {
+ dictionary.Add(keySelector(value), value);
+ }
+ return dictionary;
+ }
+
+ ///
+ /// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input. No checking for other types.
+ ///
+ private static Dictionary ToDictionaryFastNoCheck(IList list, Func keySelector, IEqualityComparer comparer)
+ {
+ Contract.Assert(list != null);
+ Contract.Assert(keySelector != null);
+
+ int listCount = list.Count;
+ Dictionary dictionary = new Dictionary(listCount, comparer);
+ for (int i = 0; i < listCount; i++)
+ {
+ TValue value = list[i];
+ dictionary.Add(keySelector(value), value);
+ }
+ return dictionary;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ContentNegotiationResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ContentNegotiationResult.cs
new file mode 100644
index 0000000000..1db747bba8
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ContentNegotiationResult.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using Microsoft.AspNet.Mvc;
+using System.Collections.Generic;
+using System.Net.Http.Headers;
+using System.Web.Http;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Represents the result of content negotiation performed using
+ ///
+ ///
+ public class ContentNegotiationResult
+ {
+ private MediaTypeFormatter _formatter;
+
+ ///
+ /// Create the content negotiation result object.
+ ///
+ /// The formatter.
+ /// The preferred media type. Can be null.
+ public ContentNegotiationResult([NotNull] MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType)
+ {
+ _formatter = formatter;
+ MediaType = mediaType;
+ }
+
+ ///
+ /// The formatter chosen for serialization.
+ ///
+ public MediaTypeFormatter Formatter
+ {
+ get { return _formatter; }
+ set
+ {
+ _formatter = value;
+ }
+ }
+
+ ///
+ /// The media type that is associated with the formatter chosen for serialization. Can be null.
+ ///
+ public MediaTypeHeaderValue MediaType { get; set; }
+ }
+}
+#endif
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/DefaultContentNegotiator.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/DefaultContentNegotiator.cs
new file mode 100644
index 0000000000..d8d8c5e1cd
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/DefaultContentNegotiator.cs
@@ -0,0 +1,460 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Web.Http;
+using Microsoft.AspNet.Mvc;
+using System.Net.Http.Formatting;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Class that selects a for an
+ /// or .
+ ///
+ public class DefaultContentNegotiator : IContentNegotiator
+ {
+ public DefaultContentNegotiator()
+ : this(false)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the with
+ /// the given setting for .
+ ///
+ ///
+ /// If ExcludeMatchOnTypeOnly is true then we don't match on type only which means
+ /// that we return null if we can't match on anything in the request. This is useful
+ /// for generating 406 (Not Acceptable) status codes.
+ ///
+ public DefaultContentNegotiator(bool excludeMatchOnTypeOnly)
+ {
+ ExcludeMatchOnTypeOnly = excludeMatchOnTypeOnly;
+ }
+
+ ///
+ /// If ExcludeMatchOnTypeOnly is true then we don't match on type only which means
+ /// that we return null if we can't match on anything in the request. This is useful
+ /// for generating 406 (Not Acceptable) status codes.
+ ///
+ public bool ExcludeMatchOnTypeOnly { get; private set; }
+
+ ///
+ /// Performs content negotiating by selecting the most appropriate out of the passed in
+ /// for the given that can serialize an object of the given
+ /// .
+ ///
+ /// The type to be serialized.
+ /// The request.
+ /// The set of objects from which to choose.
+ /// The result of the negotiation containing the most appropriate instance,
+ /// or null if there is no appropriate formatter.
+ public virtual ContentNegotiationResult Negotiate([NotNull] Type type, [NotNull] HttpRequestMessage request, [NotNull] IEnumerable formatters)
+ {
+ // Go through each formatter to compute how well it matches.
+ Collection matches = ComputeFormatterMatches(type, request, formatters);
+
+ // Select best formatter match among the matches
+ MediaTypeFormatterMatch bestFormatterMatch = SelectResponseMediaTypeFormatter(matches);
+
+ // We found a best formatter
+ if (bestFormatterMatch != null)
+ {
+ // Find the best character encoding for the selected formatter
+ Encoding bestEncodingMatch = SelectResponseCharacterEncoding(request, bestFormatterMatch.Formatter);
+ if (bestEncodingMatch != null)
+ {
+ bestFormatterMatch.MediaType.CharSet = bestEncodingMatch.WebName;
+ }
+
+ MediaTypeHeaderValue bestMediaType = bestFormatterMatch.MediaType;
+ MediaTypeFormatter bestFormatter = bestFormatterMatch.Formatter.GetPerRequestFormatterInstance(type, request, bestMediaType);
+ return new ContentNegotiationResult(bestFormatter, bestMediaType);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Determine how well each formatter matches by associating a 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.
+ ///
+ /// The type to be serialized.
+ /// The request.
+ /// The set of objects from which to choose.
+ /// A collection containing all the matches.
+ protected virtual Collection ComputeFormatterMatches([NotNull] Type type, [NotNull] HttpRequestMessage request, [NotNull] IEnumerable formatters)
+ {
+ IEnumerable sortedAcceptValues = null;
+
+ // Go through each formatter to find how well it matches.
+ ListWrapperCollection matches = new ListWrapperCollection();
+ 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 the accept header values.
+ if (sortedAcceptValues == null)
+ {
+ // Sort the Accept header values in descending order based on q-factor
+ sortedAcceptValues = SortMediaTypeWithQualityHeaderValuesByQFactor(request.Headers.Accept);
+ }
+ if ((match = MatchAcceptHeader(sortedAcceptValues, formatter)) != null)
+ {
+ matches.Add(match);
+ continue;
+ }
+
+ // Match against request's media type if any
+ if ((match = 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 = ShouldMatchOnType(sortedAcceptValues);
+
+ // Match against the type of object we are writing out
+ if (shouldMatchOnType && (match = MatchType(type, formatter)) != null)
+ {
+ matches.Add(match);
+ continue;
+ }
+ }
+
+ return matches;
+ }
+
+ ///
+ /// Select the best match among the candidate matches found.
+ ///
+ /// The collection of matches.
+ /// The determined to be the best match.
+ protected virtual MediaTypeFormatterMatch SelectResponseMediaTypeFormatter([NotNull] ICollection matches)
+ {
+ // Performance-sensitive
+
+ List 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.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 = 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 = 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 = 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 = UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderLiteral);
+ mappingOverride = UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderSubtypeMediaRange);
+ mappingOverride = 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 = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderLiteral);
+ bestMatch = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderSubtypeMediaRange);
+ bestMatch = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderAllMediaRange);
+ }
+ else if (bestMatchOnRequestMediaType != null)
+ {
+ bestMatch = bestMatchOnRequestMediaType;
+ }
+ else if (bestMatchOnType != null)
+ {
+ bestMatch = bestMatchOnType;
+ }
+
+ return bestMatch;
+ }
+
+ ///
+ /// Determine the best character encoding for writing the response. First we look
+ /// for accept-charset headers and if not found then we try to match
+ /// any charset encoding in the request (in case of PUT, POST, etc.)
+ /// If no encoding is found then we use the default for the formatter.
+ ///
+ /// The determined to be the best match.
+ protected virtual Encoding SelectResponseCharacterEncoding([NotNull] HttpRequestMessage request, [NotNull] MediaTypeFormatter formatter)
+ {
+ // If there are any SupportedEncodings then we pick an encoding
+ List supportedEncodings = formatter.SupportedEncodings.ToList();
+ if (supportedEncodings.Count > 0)
+ {
+ // Sort Accept-Charset header values
+ IEnumerable sortedAcceptCharsetValues = SortStringWithQualityHeaderValuesByQFactor(request.Headers.AcceptCharset);
+
+ // Check for match based on accept-charset headers
+ foreach (StringWithQualityHeaderValue acceptCharset in sortedAcceptCharsetValues)
+ {
+ for (int i = 0; i < supportedEncodings.Count; i++)
+ {
+ Encoding encoding = supportedEncodings[i];
+ if (encoding != null && acceptCharset.Quality != FormattingUtilities.NoMatch &&
+ (acceptCharset.Value.Equals(encoding.WebName, StringComparison.OrdinalIgnoreCase) ||
+ acceptCharset.Value.Equals("*", StringComparison.OrdinalIgnoreCase)))
+ {
+ return encoding;
+ }
+ }
+ }
+
+ // Check for match based on any request entity body
+ return formatter.SelectCharacterEncoding(request.Content != null ? request.Content.Headers : null);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Match the request accept header field values against the formatter's registered supported media types.
+ ///
+ /// The sorted accept header values to match.
+ /// The formatter to match against.
+ /// A indicating the quality of the match or null is no match.
+ protected virtual MediaTypeFormatterMatch MatchAcceptHeader([NotNull] IEnumerable sortedAcceptValues, [NotNull] MediaTypeFormatter formatter)
+ {
+ foreach (MediaTypeWithQualityHeaderValue acceptMediaTypeValue in sortedAcceptValues)
+ {
+ List supportedMediaTypes = formatter.SupportedMediaTypes.ToList();
+ for (int i = 0; i < supportedMediaTypes.Count; i++)
+ {
+ MediaTypeHeaderValue supportedMediaType = supportedMediaTypes[i];
+ MediaTypeHeaderValueRange range;
+ if (supportedMediaType != null && acceptMediaTypeValue.Quality != FormattingUtilities.NoMatch &&
+ supportedMediaType.IsSubsetOf(acceptMediaTypeValue, out range))
+ {
+ MediaTypeFormatterMatchRanking ranking;
+ switch (range)
+ {
+ case MediaTypeHeaderValueRange.AllMediaRange:
+ ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange;
+ break;
+
+ case MediaTypeHeaderValueRange.SubtypeMediaRange:
+ ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange;
+ break;
+
+ default:
+ ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral;
+ break;
+ }
+
+ return new MediaTypeFormatterMatch(formatter, supportedMediaType, acceptMediaTypeValue.Quality, ranking);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Match any request media type (in case there is a request entity body) against the formatter's registered
+ /// media types.
+ ///
+ /// The request to match.
+ /// The formatter to match against.
+ /// A indicating the quality of the match or null is no match.
+ protected virtual MediaTypeFormatterMatch MatchRequestMediaType([NotNull] HttpRequestMessage request, [NotNull] MediaTypeFormatter formatter)
+ {
+ if (request.Content != null)
+ {
+ MediaTypeHeaderValue requestMediaType = request.Content.Headers.ContentType;
+ if (requestMediaType != null)
+ {
+ List supportedMediaTypes = formatter.SupportedMediaTypes.ToList();
+ for (int i = 0; i < supportedMediaTypes.Count; i++)
+ {
+ MediaTypeHeaderValue supportedMediaType = supportedMediaTypes[i];
+ if (supportedMediaType != null && supportedMediaType.IsSubsetOf(requestMediaType))
+ {
+ return new MediaTypeFormatterMatch(formatter, supportedMediaType, FormattingUtilities.Match, MediaTypeFormatterMatchRanking.MatchOnRequestMediaType);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Determine whether to match on type or not. This is used to determine whether to
+ /// generate a 406 response or use the default media type formatter in case there
+ /// is no match against anything in the request. If ExcludeMatchOnTypeOnly is true
+ /// then we don't match on type unless there are no accept headers.
+ ///
+ /// The sorted accept header values to match.
+ /// True if not ExcludeMatchOnTypeOnly and accept headers with a q-factor bigger than 0.0 are present.
+ protected virtual bool ShouldMatchOnType([NotNull] IEnumerable sortedAcceptValues)
+ {
+ return !(ExcludeMatchOnTypeOnly && sortedAcceptValues.Any());
+ }
+
+ ///
+ /// Pick the first supported media type and indicate we've matched only on type
+ ///
+ /// The type to be serialized.
+ /// The formatter we are matching against.
+ /// A indicating the quality of the match or null is no match.
+ protected virtual MediaTypeFormatterMatch MatchType([NotNull] Type type, [NotNull] MediaTypeFormatter formatter)
+ {
+ // We already know that we do match on type -- otherwise we wouldn't even be called --
+ // so this is just a matter of determining how we match.
+ MediaTypeHeaderValue mediaType = null;
+ List supportedMediaTypes = formatter.SupportedMediaTypes.ToList();
+ if (supportedMediaTypes.Count > 0)
+ {
+ mediaType = supportedMediaTypes[0];
+ }
+ return new MediaTypeFormatterMatch(formatter, mediaType, FormattingUtilities.Match, MediaTypeFormatterMatchRanking.MatchOnCanWriteType);
+ }
+
+ ///
+ /// Sort Accept header values and related header field values with similar syntax rules
+ /// (if more than 1) in descending order based on q-factor.
+ ///
+ /// The header values to sort.
+ /// The sorted header values.
+ protected virtual IEnumerable SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection headerValues)
+ {
+ if (headerValues.Count > 1)
+ {
+ // Use OrderBy() instead of Array.Sort() as it performs fewer comparisons. In this case the comparisons
+ // are quite expensive so OrderBy() performs better.
+ return headerValues.OrderByDescending(m => m, MediaTypeWithQualityHeaderValueComparer.QualityComparer).ToArray();
+ }
+ else
+ {
+ return headerValues;
+ }
+ }
+
+ ///
+ /// Sort Accept-Charset, Accept-Encoding, Accept-Language and related header field values with similar syntax rules
+ /// (if more than 1) in descending order based on q-factor.
+ ///
+ /// The header values to sort.
+ /// The sorted header values.
+ protected virtual IEnumerable SortStringWithQualityHeaderValuesByQFactor([NotNull] ICollection headerValues)
+ {
+ if (headerValues.Count > 1)
+ {
+ // Use OrderBy() instead of Array.Sort() as it performs fewer comparisons. In this case the comparisons
+ // are quite expensive so OrderBy() performs better.
+ return headerValues.OrderByDescending(m => m, StringWithQualityHeaderValueComparer.QualityComparer).ToArray();
+ }
+ else
+ {
+ return headerValues;
+ }
+ }
+
+ ///
+ /// Evaluates whether a match is better than the current match and if so returns the replacement; otherwise returns the
+ /// current match.
+ ///
+ 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;
+ }
+
+ private static MediaTypeFormatter[] GetWritingFormatters(IEnumerable formatters)
+ {
+ Contract.Assert(formatters != null);
+ MediaTypeFormatterCollection formatterCollection = formatters as MediaTypeFormatterCollection;
+ return formatters.AsArray();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormDataCollection.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormDataCollection.cs
new file mode 100644
index 0000000000..e47e012654
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormDataCollection.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using Microsoft.AspNet.WebUtilities;
+using System;
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Net.Http.Formatting
+{
+ public class FormDataCollection : IEnumerable>
+ {
+ private readonly IList> _values;
+
+ public FormDataCollection(string query)
+ {
+ var parsedQuery = QueryHelpers.ParseQuery(query);
+
+ var values = new List>();
+ foreach (var kvp in parsedQuery)
+ {
+ foreach (var value in kvp.Value)
+ {
+ values.Add(new KeyValuePair(kvp.Key, value));
+ }
+ }
+
+ _values = values;
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return _values.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _values.GetEnumerator();
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormattingUtilities.cs
new file mode 100644
index 0000000000..82fec1ad8c
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/FormattingUtilities.cs
@@ -0,0 +1,241 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net.Http.Formatting;
+using System.Net.Http.Headers;
+using System.Runtime.Serialization;
+using System.Xml;
+using Newtonsoft.Json.Linq;
+using System.Reflection;
+
+namespace System.Net.Http
+{
+ ///
+ /// Provides various internal utility functions
+ ///
+ internal static class FormattingUtilities
+ {
+ // Supported date formats for input.
+ private static readonly string[] dateFormats = new string[]
+ {
+ // "r", // RFC 1123, required output format but too strict for input
+ "ddd, d MMM yyyy H:m:s 'GMT'", // RFC 1123 (r, except it allows both 1 and 01 for date and time)
+ "ddd, d MMM yyyy H:m:s", // RFC 1123, no zone - assume GMT
+ "d MMM yyyy H:m:s 'GMT'", // RFC 1123, no day-of-week
+ "d MMM yyyy H:m:s", // RFC 1123, no day-of-week, no zone
+ "ddd, d MMM yy H:m:s 'GMT'", // RFC 1123, short year
+ "ddd, d MMM yy H:m:s", // RFC 1123, short year, no zone
+ "d MMM yy H:m:s 'GMT'", // RFC 1123, no day-of-week, short year
+ "d MMM yy H:m:s", // RFC 1123, no day-of-week, short year, no zone
+
+ "dddd, d'-'MMM'-'yy H:m:s 'GMT'", // RFC 850
+ "dddd, d'-'MMM'-'yy H:m:s", // RFC 850 no zone
+ "ddd MMM d H:m:s yyyy", // ANSI C's asctime() format
+
+ "ddd, d MMM yyyy H:m:s zzz", // RFC 5322
+ "ddd, d MMM yyyy H:m:s", // RFC 5322 no zone
+ "d MMM yyyy H:m:s zzz", // RFC 5322 no day-of-week
+ "d MMM yyyy H:m:s", // RFC 5322 no day-of-week, no zone
+ };
+
+ // Valid header token characters are within the range 0x20 < c < 0x7F excluding the following characters
+ private const string NonTokenChars = "()<>@,;:\\\"/[]?={}";
+
+ ///
+ /// Quality factor to indicate a perfect match.
+ ///
+ public const double Match = 1.0;
+
+ ///
+ /// Quality factor to indicate no match.
+ ///
+ public const double NoMatch = 0.0;
+
+ ///
+ /// The default max depth for our formatter is 256
+ ///
+ public const int DefaultMaxDepth = 256;
+
+ ///
+ /// The default min depth for our formatter is 1
+ ///
+ public const int DefaultMinDepth = 1;
+
+ ///
+ /// HTTP X-Requested-With header field name
+ ///
+ public const string HttpRequestedWithHeader = @"x-requested-with";
+
+ ///
+ /// HTTP X-Requested-With header field value
+ ///
+ public const string HttpRequestedWithHeaderValue = @"XMLHttpRequest";
+
+ ///
+ /// HTTP Host header field name
+ ///
+ public const string HttpHostHeader = "Host";
+
+ ///
+ /// HTTP Version token
+ ///
+ public const string HttpVersionToken = "HTTP";
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type HttpRequestMessageType = typeof(HttpRequestMessage);
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type HttpResponseMessageType = typeof(HttpResponseMessage);
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type HttpContentType = typeof(HttpContent);
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type DelegatingEnumerableGenericType = typeof(DelegatingEnumerable<>);
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type EnumerableInterfaceGenericType = typeof(IEnumerable<>);
+
+ ///
+ /// A representing .
+ ///
+ public static readonly Type QueryableInterfaceGenericType = typeof(IQueryable<>);
+
+ ///
+ /// Determines whether is a type.
+ ///
+ /// The type to test.
+ ///
+ /// true if is a type; otherwise, false.
+ ///
+ public static bool IsJTokenType(Type type)
+ {
+ return typeof(JToken).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo());
+ }
+
+ ///
+ /// Creates an empty instance. The only way is to get it from a dummy
+ /// instance.
+ ///
+ /// The created instance.
+ public static HttpContentHeaders CreateEmptyContentHeaders()
+ {
+ HttpContent tempContent = null;
+ HttpContentHeaders contentHeaders = null;
+ try
+ {
+ tempContent = new StringContent(String.Empty);
+ contentHeaders = tempContent.Headers;
+ contentHeaders.Clear();
+ }
+ finally
+ {
+ // We can dispose the content without touching the headers
+ if (tempContent != null)
+ {
+ tempContent.Dispose();
+ }
+ }
+
+ return contentHeaders;
+ }
+
+ ///
+ /// Create a default reader quotas with a default depth quota of 1K
+ ///
+ ///
+ public static XmlDictionaryReaderQuotas CreateDefaultReaderQuotas()
+ {
+#if NETFX_CORE // MaxDepth is a DOS mitigation. We don't support MaxDepth in portable libraries because it is strictly client side.
+ return XmlDictionaryReaderQuotas.Max;
+#else
+ return new XmlDictionaryReaderQuotas()
+ {
+ MaxArrayLength = Int32.MaxValue,
+ MaxBytesPerRead = Int32.MaxValue,
+ MaxDepth = DefaultMaxDepth,
+ MaxNameTableCharCount = Int32.MaxValue,
+ MaxStringContentLength = Int32.MaxValue
+ };
+#endif
+ }
+
+ ///
+ /// Remove bounding quotes on a token if present
+ ///
+ /// Token to unquote.
+ /// Unquoted token.
+ public static string UnquoteToken(string token)
+ {
+ if (String.IsNullOrWhiteSpace(token))
+ {
+ return token;
+ }
+
+ if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
+ {
+ return token.Substring(1, token.Length - 2);
+ }
+
+ return token;
+ }
+
+ public static bool ValidateHeaderToken(string token)
+ {
+ if (token == null)
+ {
+ return false;
+ }
+
+ foreach (char c in token)
+ {
+ if (c < 0x21 || c > 0x7E || NonTokenChars.IndexOf(c) != -1)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static string DateToString(DateTimeOffset dateTime)
+ {
+ // Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo)
+ return dateTime.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture);
+ }
+
+ public static bool TryParseDate(string input, out DateTimeOffset result)
+ {
+ return DateTimeOffset.TryParseExact(input, dateFormats, DateTimeFormatInfo.InvariantInfo,
+ DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal,
+ out result);
+ }
+
+ ///
+ /// Parses valid integer strings with no leading signs, whitespace or other
+ ///
+ /// The value to parse
+ /// The result
+ /// True if value was valid; false otherwise.
+ public static bool TryParseInt32(string value, out int result)
+ {
+ return Int32.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/IContentNegotiator.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/IContentNegotiator.cs
new file mode 100644
index 0000000000..a51371cea0
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/IContentNegotiator.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Performs content negotiation.
+ /// This is the process of selecting a response writer (formatter) in compliance with header values in the request.
+ ///
+ public interface IContentNegotiator
+ {
+ ///
+ /// Performs content negotiating by selecting the most appropriate out of the passed in
+ /// for the given that can serialize an object of the given
+ /// .
+ ///
+ ///
+ /// Implementations of this method should call
+ /// on the selected formatter and return the result of that method.
+ ///
+ /// The type to be serialized.
+ /// Request message, which contains the header values used to perform negotiation.
+ /// The set of objects from which to choose.
+ /// The result of the negotiation containing the most appropriate instance,
+ /// or null if there is no appropriate formatter.
+ ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable formatters);
+ }
+}
+#endif
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ListWrapperCollection.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ListWrapperCollection.cs
new file mode 100644
index 0000000000..aed400241c
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ListWrapperCollection.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+
+namespace System.Collections.ObjectModel
+{
+ ///
+ /// A class that inherits from Collection of T but also exposes its underlying data as List of T for performance.
+ ///
+ internal sealed class ListWrapperCollection : Collection
+ {
+ private readonly List _items;
+
+ internal ListWrapperCollection()
+ : this(new List())
+ {
+ }
+
+ internal ListWrapperCollection(List list)
+ : base(list)
+ {
+ _items = list;
+ }
+
+ internal List ItemsList
+ {
+ get { return _items; }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeConstants.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeConstants.cs
new file mode 100644
index 0000000000..d78f6f66aa
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeConstants.cs
@@ -0,0 +1,105 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Constants related to media types.
+ ///
+ internal static class MediaTypeConstants
+ {
+ private static readonly MediaTypeHeaderValue _defaultApplicationXmlMediaType = new MediaTypeHeaderValue("application/xml");
+ private static readonly MediaTypeHeaderValue _defaultTextXmlMediaType = new MediaTypeHeaderValue("text/xml");
+ private static readonly MediaTypeHeaderValue _defaultApplicationJsonMediaType = new MediaTypeHeaderValue("application/json");
+ private static readonly MediaTypeHeaderValue _defaultTextJsonMediaType = new MediaTypeHeaderValue("text/json");
+ private static readonly MediaTypeHeaderValue _defaultApplicationOctetStreamMediaType = new MediaTypeHeaderValue("application/octet-stream");
+ private static readonly MediaTypeHeaderValue _defaultApplicationFormUrlEncodedMediaType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
+ private static readonly MediaTypeHeaderValue _defaultApplicationBsonMediaType = new MediaTypeHeaderValue("application/bson");
+
+ ///
+ /// Gets a instance representing application/octet-stream.
+ ///
+ ///
+ /// A new instance representing application/octet-stream.
+ ///
+ public static MediaTypeHeaderValue ApplicationOctetStreamMediaType
+ {
+ get { return _defaultApplicationOctetStreamMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/xml.
+ ///
+ ///
+ /// A new instance representing application/xml.
+ ///
+ public static MediaTypeHeaderValue ApplicationXmlMediaType
+ {
+ get { return _defaultApplicationXmlMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/json.
+ ///
+ ///
+ /// A new instance representing application/json.
+ ///
+ public static MediaTypeHeaderValue ApplicationJsonMediaType
+ {
+ get { return _defaultApplicationJsonMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing text/xml.
+ ///
+ ///
+ /// A new instance representing text/xml.
+ ///
+ public static MediaTypeHeaderValue TextXmlMediaType
+ {
+ get { return _defaultTextXmlMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing text/json.
+ ///
+ ///
+ /// A new instance representing text/json.
+ ///
+ public static MediaTypeHeaderValue TextJsonMediaType
+ {
+ get { return _defaultTextJsonMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/x-www-form-urlencoded.
+ ///
+ ///
+ /// A new instance representing application/x-www-form-urlencoded.
+ ///
+ public static MediaTypeHeaderValue ApplicationFormUrlEncodedMediaType
+ {
+ get { return _defaultApplicationFormUrlEncodedMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/bson.
+ ///
+ ///
+ /// A new instance representing application/bson.
+ ///
+ ///
+ /// Not yet a standard. In particular this media type is not currently listed at
+ /// http://www.iana.org/assignments/media-types/application.
+ ///
+ public static MediaTypeHeaderValue ApplicationBsonMediaType
+ {
+ get { return _defaultApplicationBsonMediaType; }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatch.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatch.cs
new file mode 100644
index 0000000000..12739383c1
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatch.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Net.Http.Headers;
+using System.Web.Http;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// This class describes how well a particular matches a request.
+ ///
+ public class MediaTypeFormatterMatch
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The matching formatter.
+ /// The media type. Can be null in which case the media type application/octet-stream is used.
+ /// The quality of the match. Can be null in which case it is considered a full match with a value of 1.0
+ /// The kind of match.
+ public MediaTypeFormatterMatch(MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, double? quality, MediaTypeFormatterMatchRanking ranking)
+ {
+ Formatter = formatter;
+ MediaType = mediaType != null ? mediaType : MediaTypeConstants.ApplicationOctetStreamMediaType;
+ Quality = quality ?? FormattingUtilities.Match;
+ Ranking = ranking;
+ }
+
+ ///
+ /// Gets the media type formatter.
+ ///
+ public MediaTypeFormatter Formatter { get; private set; }
+
+ ///
+ /// Gets the matched media type.
+ ///
+ public MediaTypeHeaderValue MediaType { get; private set; }
+
+ ///
+ /// Gets the quality of the match
+ ///
+ public double Quality { get; private set; }
+
+ ///
+ /// Gets the kind of match that occurred.
+ ///
+ public MediaTypeFormatterMatchRanking Ranking { get; private set; }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatchRanking.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatchRanking.cs
new file mode 100644
index 0000000000..11b23a1484
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeFormatterMatchRanking.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Contains information about the degree to which a matches the
+ /// explicit or implicit preferences found in an incoming request.
+ ///
+ public enum MediaTypeFormatterMatchRanking
+ {
+ ///
+ /// No match was found
+ ///
+ None = 0,
+
+ ///
+ /// Matched on type meaning that the formatter is able to serialize the type
+ ///
+ MatchOnCanWriteType,
+
+ ///
+ /// Matched on explicit literal accept header in ,
+ /// e.g. "application/json".
+ ///
+ MatchOnRequestAcceptHeaderLiteral,
+
+ ///
+ /// Matched on explicit subtype range accept header in ,
+ /// e.g. "application/*".
+ ///
+ MatchOnRequestAcceptHeaderSubtypeMediaRange,
+
+ ///
+ /// Matched on explicit all media type range accept header in ,
+ /// e.g. "*/*"
+ ///
+ MatchOnRequestAcceptHeaderAllMediaRange,
+
+ ///
+ /// Matched on the media type of the of the .
+ ///
+ MatchOnRequestMediaType,
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeHeaderValueExtensions.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeHeaderValueExtensions.cs
new file mode 100644
index 0000000000..beb2b7a6cf
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeHeaderValueExtensions.cs
@@ -0,0 +1,110 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using Microsoft.AspNet.Mvc;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Extension methods for .
+ ///
+ internal static class MediaTypeHeaderValueExtensions
+ {
+ ///
+ /// Determines whether two instances match. The instance
+ /// is said to match if and only if
+ /// is a strict subset of the values and parameters of .
+ /// That is, if the media type and media type parameters of are all present
+ /// and match those of then it is a match even though may have additional
+ /// parameters.
+ ///
+ /// The first media type.
+ /// The second media type.
+ /// true if this is a subset of ; false otherwise.
+ public static bool IsSubsetOf(this MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2)
+ {
+ MediaTypeHeaderValueRange mediaType2Range;
+ return IsSubsetOf(mediaType1, mediaType2, out mediaType2Range);
+ }
+
+ ///
+ /// Determines whether two instances match. The instance
+ /// is said to match if and only if
+ /// is a strict subset of the values and parameters of .
+ /// That is, if the media type and media type parameters of are all present
+ /// and match those of then it is a match even though may have additional
+ /// parameters.
+ ///
+ /// The first media type.
+ /// The second media type.
+ /// Indicates whether is a regular media type, a subtype media range, or a full media range
+ /// true if this is a subset of ; false otherwise.
+ public static bool IsSubsetOf(this MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2, out MediaTypeHeaderValueRange mediaType2Range)
+ {
+ // Performance-sensitive
+ Contract.Assert(mediaType1 != null);
+
+ if (mediaType2 == null)
+ {
+ mediaType2Range = MediaTypeHeaderValueRange.None;
+ return false;
+ }
+
+ ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1);
+ ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2);
+ mediaType2Range = parsedMediaType2.IsAllMediaRange ? MediaTypeHeaderValueRange.AllMediaRange :
+ parsedMediaType2.IsSubtypeMediaRange ? MediaTypeHeaderValueRange.SubtypeMediaRange :
+ MediaTypeHeaderValueRange.None;
+
+ if (!parsedMediaType1.TypesEqual(ref parsedMediaType2))
+ {
+ if (mediaType2Range != MediaTypeHeaderValueRange.AllMediaRange)
+ {
+ return false;
+ }
+ }
+ else if (!parsedMediaType1.SubTypesEqual(ref parsedMediaType2))
+ {
+ if (mediaType2Range != MediaTypeHeaderValueRange.SubtypeMediaRange)
+ {
+ return false;
+ }
+ }
+
+ // So far we either have a full match or a subset match. Now check that all of
+ // mediaType1's parameters are present and equal in mediatype2
+ // Optimize for the common case where the parameters inherit from Collection and cache the count which is faster for Collection.
+ Collection parameters1 = mediaType1.Parameters.AsCollection();
+ int parameterCount1 = parameters1.Count;
+ Collection parameters2 = mediaType2.Parameters.AsCollection();
+ int parameterCount2 = parameters2.Count;
+ for (int i = 0; i < parameterCount1; i++)
+ {
+ NameValueHeaderValue parameter1 = parameters1[i];
+ bool found = false;
+ for (int j = 0; j < parameterCount2; j++)
+ {
+ NameValueHeaderValue parameter2 = parameters2[j];
+ if (parameter1.Equals(parameter2))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeWithQualityHeaderComparer.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeWithQualityHeaderComparer.cs
new file mode 100644
index 0000000000..8ac2c8d48b
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/MediaTypeWithQualityHeaderComparer.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ /// Implementation of that can compare accept media type header fields
+ /// based on their quality values (a.k.a q-values). See
+ /// for a comparer for other content negotiation
+ /// header field q-values.
+ internal class MediaTypeWithQualityHeaderValueComparer : IComparer
+ {
+ private static readonly MediaTypeWithQualityHeaderValueComparer _mediaTypeComparer = new MediaTypeWithQualityHeaderValueComparer();
+
+ private MediaTypeWithQualityHeaderValueComparer()
+ {
+ }
+
+ public static MediaTypeWithQualityHeaderValueComparer QualityComparer
+ {
+ get { return _mediaTypeComparer; }
+ }
+
+ ///
+ /// Compares two based on their quality value (a.k.a their "q-value").
+ /// Values with identical q-values are considered equal (i.e the result is 0) with the exception that sub-type wild-cards are
+ /// considered less than specific media types and full wild-cards are considered less than sub-type wild-cards. This allows to
+ /// sort a sequence of following their q-values in the order of specific media types,
+ /// sub-type wildcards, and last any full wild-cards.
+ ///
+ /// The first to compare.
+ /// The second to compare.
+ ///
+ public int Compare(MediaTypeWithQualityHeaderValue mediaType1, MediaTypeWithQualityHeaderValue mediaType2)
+ {
+ Contract.Assert(mediaType1 != null, "The 'mediaType1' parameter should not be null.");
+ Contract.Assert(mediaType2 != null, "The 'mediaType2' parameter should not be null.");
+
+ if (Object.ReferenceEquals(mediaType1, mediaType2))
+ {
+ return 0;
+ }
+
+ int returnValue = CompareBasedOnQualityFactor(mediaType1, mediaType2);
+
+ if (returnValue == 0)
+ {
+ ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1);
+ ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2);
+
+ if (!parsedMediaType1.TypesEqual(ref parsedMediaType2))
+ {
+ if (parsedMediaType1.IsAllMediaRange)
+ {
+ return -1;
+ }
+ else if (parsedMediaType2.IsAllMediaRange)
+ {
+ return 1;
+ }
+ else if (parsedMediaType1.IsSubtypeMediaRange && !parsedMediaType2.IsSubtypeMediaRange)
+ {
+ return -1;
+ }
+ else if (!parsedMediaType1.IsSubtypeMediaRange && parsedMediaType2.IsSubtypeMediaRange)
+ {
+ return 1;
+ }
+ }
+ else if (!parsedMediaType1.SubTypesEqual(ref parsedMediaType2))
+ {
+ if (parsedMediaType1.IsSubtypeMediaRange)
+ {
+ return -1;
+ }
+ else if (parsedMediaType2.IsSubtypeMediaRange)
+ {
+ return 1;
+ }
+ }
+ }
+
+ return returnValue;
+ }
+
+ private static int CompareBasedOnQualityFactor(MediaTypeWithQualityHeaderValue mediaType1, MediaTypeWithQualityHeaderValue mediaType2)
+ {
+ Contract.Assert(mediaType1 != null);
+ Contract.Assert(mediaType2 != null);
+
+ double mediaType1Quality = mediaType1.Quality ?? FormattingUtilities.Match;
+ double mediaType2Quality = mediaType2.Quality ?? FormattingUtilities.Match;
+ double qualityDifference = mediaType1Quality - mediaType2Quality;
+ if (qualityDifference < 0)
+ {
+ return -1;
+ }
+ else if (qualityDifference > 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ParsedMediaTypeHeaderValue.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ParsedMediaTypeHeaderValue.cs
new file mode 100644
index 0000000000..d0b966789b
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/ParsedMediaTypeHeaderValue.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Diagnostics.Contracts;
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ // This type is instanciated by frequently called comparison methods so is very performance sensitive
+ internal struct ParsedMediaTypeHeaderValue
+ {
+ private const char MediaRangeAsterisk = '*';
+ private const char MediaTypeSubtypeDelimiter = '/';
+
+ private readonly string _mediaType;
+ private readonly int _delimiterIndex;
+ private readonly bool _isAllMediaRange;
+ private readonly bool _isSubtypeMediaRange;
+
+ public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaTypeHeaderValue)
+ {
+ Contract.Assert(mediaTypeHeaderValue != null);
+ string mediaType = _mediaType = mediaTypeHeaderValue.MediaType;
+ _delimiterIndex = mediaType.IndexOf(MediaTypeSubtypeDelimiter);
+ Contract.Assert(_delimiterIndex > 0, "The constructor of the MediaTypeHeaderValue would have failed if there wasn't a type and subtype.");
+
+ _isAllMediaRange = false;
+ _isSubtypeMediaRange = false;
+ int mediaTypeLength = mediaType.Length;
+ if (_delimiterIndex == mediaTypeLength - 2)
+ {
+ if (mediaType[mediaTypeLength - 1] == MediaRangeAsterisk)
+ {
+ _isSubtypeMediaRange = true;
+ if (_delimiterIndex == 1 && mediaType[0] == MediaRangeAsterisk)
+ {
+ _isAllMediaRange = true;
+ }
+ }
+ }
+ }
+
+ public bool IsAllMediaRange
+ {
+ get { return _isAllMediaRange; }
+ }
+
+ public bool IsSubtypeMediaRange
+ {
+ get { return _isSubtypeMediaRange; }
+ }
+
+ public bool TypesEqual(ref ParsedMediaTypeHeaderValue other)
+ {
+ if (_delimiterIndex != other._delimiterIndex)
+ {
+ return false;
+ }
+ return String.Compare(_mediaType, 0, other._mediaType, 0, _delimiterIndex, StringComparison.OrdinalIgnoreCase) == 0;
+ }
+
+ public bool SubTypesEqual(ref ParsedMediaTypeHeaderValue other)
+ {
+ int _subTypeLength = _mediaType.Length - _delimiterIndex - 1;
+ if (_subTypeLength != other._mediaType.Length - other._delimiterIndex - 1)
+ {
+ return false;
+ }
+ return String.Compare(_mediaType, _delimiterIndex + 1, other._mediaType, other._delimiterIndex + 1, _subTypeLength, StringComparison.OrdinalIgnoreCase) == 0;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/StringWithQualityHeaderValueComparer.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/StringWithQualityHeaderValueComparer.cs
new file mode 100644
index 0000000000..89a84a3a0e
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ContentNegotiator/StringWithQualityHeaderValueComparer.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if ASPNETCORE50
+
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Implementation of that can compare content negotiation header fields
+ /// based on their quality values (a.k.a q-values). This applies to values used in accept-charset,
+ /// accept-encoding, accept-language and related header fields with similar syntax rules. See
+ /// for a comparer for media type
+ /// q-values.
+ ///
+ internal class StringWithQualityHeaderValueComparer : IComparer
+ {
+ private static readonly StringWithQualityHeaderValueComparer _qualityComparer =
+ new StringWithQualityHeaderValueComparer();
+
+ private StringWithQualityHeaderValueComparer()
+ {
+ }
+
+ public static StringWithQualityHeaderValueComparer QualityComparer
+ {
+ get { return _qualityComparer; }
+ }
+
+ ///
+ /// Compares two based on their quality value (a.k.a their "q-value").
+ /// Values with identical q-values are considered equal (i.e the result is 0) with the exception of wild-card
+ /// values (i.e. a value of "*") which are considered less than non-wild-card values. This allows to sort
+ /// a sequence of following their q-values ending up with any
+ /// wild-cards at the end.
+ ///
+ /// The first value to compare.
+ /// The second value to compare
+ /// The result of the comparison.
+ public int Compare(StringWithQualityHeaderValue stringWithQuality1,
+ StringWithQualityHeaderValue stringWithQuality2)
+ {
+ Contract.Assert(stringWithQuality1 != null);
+ Contract.Assert(stringWithQuality2 != null);
+
+ double quality1 = stringWithQuality1.Quality ?? FormattingUtilities.Match;
+ double quality2 = stringWithQuality2.Quality ?? FormattingUtilities.Match;
+ double qualityDifference = quality1 - quality2;
+ if (qualityDifference < 0)
+ {
+ return -1;
+ }
+ else if (qualityDifference > 0)
+ {
+ return 1;
+ }
+
+ if (!String.Equals(stringWithQuality1.Value, stringWithQuality2.Value, StringComparison.OrdinalIgnoreCase))
+ {
+ if (String.Equals(stringWithQuality1.Value, "*", StringComparison.OrdinalIgnoreCase))
+ {
+ return -1;
+ }
+ else if (String.Equals(stringWithQuality2.Value, "*", StringComparison.OrdinalIgnoreCase))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageExtensions.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageExtensions.cs
index bce4ef5c99..8deae66a1e 100644
--- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageExtensions.cs
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageExtensions.cs
@@ -20,6 +20,9 @@ namespace System.Net.Http
///
public static class HttpRequestMessageExtensions
{
+
+#if !ASPNETCORE50
+
///
/// Helper method for creating an message with a "416 (Requested Range Not Satisfiable)" status code.
/// This response can be used in combination with the to indicate that the requested range or
@@ -41,6 +44,8 @@ namespace System.Net.Http
return rangeNotSatisfiableResponse;
}
+#endif
+
///
/// Helper method that performs content negotiation and creates a representing an error
/// with an instance of wrapping an with message .
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/project.json b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/project.json
index 12a0f20de6..2f729fbe7a 100644
--- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/project.json
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/project.json
@@ -1,7 +1,7 @@
{
"version": "6.0.0-*",
"compilationOptions": {
- "warningsAsErrors": true
+ "warningsAsErrors": false
},
"dependencies": {
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
@@ -13,6 +13,11 @@
"frameworkAssemblies": {
"System.Net.Http": "4.0.0.0"
}
+ },
+ "aspnetcore50": {
+ "dependencies": {
+ "System.Net.Http": "4.0.0-beta-*"
+ }
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionSelectionTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionSelectionTest.cs
index 93072ae413..719e4d3b46 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionSelectionTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionSelectionTest.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if ASPNET50
using System;
using System.Net;
using System.Net.Http;
@@ -623,5 +622,4 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
public string ControllerName { get; set; }
}
}
-}
- #endif
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs
index 1bf126c9ff..eef6773203 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-#if ASPNET50
using System;
using System.Collections.Generic;
using System.Net;
@@ -58,6 +57,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
content);
}
+#if !ASPNETCORE50
+
[Fact]
public async Task Options_SetsDefaultFormatters()
{
@@ -83,6 +84,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expected, formatters);
}
+#endif
+
[Fact]
public async Task ActionThrowsHttpResponseException_WithStatusCode()
{
@@ -521,4 +524,3 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
}
}
-#endif
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
index 72b46b1642..f1e68382fc 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
@@ -24,7 +24,8 @@
"ValueProvidersSite": "1.0.0",
"XmlSerializerWebSite": "1.0.0",
"UrlHelperWebSite": "1.0.0",
-
+ "WebApiCompatShimWebSite": "1.0.0",
+
"Microsoft.AspNet.TestHost": "1.0.0-*",
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
@@ -33,7 +34,6 @@
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
"Microsoft.Framework.Logging": "1.0.0-*",
"Microsoft.Framework.Runtime.Interfaces": "1.0.0-*",
- "Microsoft.AspNet.PipelineCore": "1.0.0-*",
"TagHelpersWebSite": "1.0.0",
"Xunit.KRunner": "1.0.0-*"
},
@@ -43,8 +43,7 @@
"frameworks": {
"aspnet50": {
"dependencies": {
- "AutofacWebSite": "1.0.0",
- "WebApiCompatShimWebSite": "1.0.0"
+ "AutofacWebSite": "1.0.0"
}
},
"aspnetcore50": {
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
index 3d96a3b3d3..58bde7f949 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+#if !ASPNETCORE50
+
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -344,4 +346,5 @@ namespace System.Web.Http.TestControllers
return null;
}
}
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/DefaultContentNegotiatorTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/DefaultContentNegotiatorTest.cs
new file mode 100644
index 0000000000..2427d659d2
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/DefaultContentNegotiatorTest.cs
@@ -0,0 +1,835 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http.Formatting.Mocks;
+using System.Net.Http.Headers;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using Xunit;
+using Microsoft.TestCommon;
+
+namespace System.Net.Http.Formatting
+{
+ public class DefaultContentNegotiatorTests
+ {
+ private readonly DefaultContentNegotiator _negotiator = new DefaultContentNegotiator();
+ private readonly HttpRequestMessage _request = new HttpRequestMessage();
+
+ public static TheoryData MatchRequestMediaTypeData
+ {
+ get
+ {
+ // string requestMediaType, string[] supportedMediaTypes, string expectedMediaType
+ return new TheoryData
+ {
+ { "text/plain", new string[0], null },
+ { "text/plain", new string[] { "text/xml", "application/xml" }, null },
+ { "application/xml", new string[] { "application/xml", "text/xml" }, "application/xml" },
+ { "APPLICATION/XML", new string[] { "text/xml", "application/xml" }, "application/xml" },
+ { "application/xml; charset=utf-8", new string[] { "text/xml", "application/xml" }, "application/xml" },
+ { "application/xml; charset=utf-8; parameter=value", new string[] { "text/xml", "application/xml" }, "application/xml" },
+ };
+ }
+ }
+
+ public static TheoryData MatchAcceptHeaderData
+ {
+ get
+ {
+ // string[] acceptHeader, string[] supportedMediaTypes, string expectedMediaType, double matchQuality, int range
+ return new TheoryData
+ {
+ { new string[] { "text/plain" }, new string[0], null, 0.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+
+ { new string[] { "text/plain" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+ { new string[] { "text/plain; q=0.5" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+
+ { new string[] { "application/xml" }, new string[] { "application/xml", "text/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "APPLICATION/XML; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "text/xml; q=0.5", "APPLICATION/XML; q=0.7" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.7, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "application/xml; q=0.0" }, new string[] { "application/xml", "text/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+ { new string[] { "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+ { new string[] { "text/xml; q=0.0", "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+
+ { new string[] { "text/*" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
+ { new string[] { "text/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "text/*", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
+ { new string[] { "text/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
+ { new string[] { "text/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "text/*; q=0.0", "application/xml; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+ { new string[] { "text/*; q=0.0", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+
+ { new string[] { "*/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange },
+ { new string[] { "*/*; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
+ { new string[] { "*/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ { new string[] { "*/*; q=1.0", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange },
+ { new string[] { "*/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+
+ { new string[] { "text/*; q=0.5", "*/*; q=0.2", "application/xml; q=1.0" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+
+ { new string[] { "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
+ };
+ }
+ }
+
+ public static TheoryData ShouldMatchOnTypeData
+ {
+ get
+ {
+ // bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult
+ return new TheoryData
+ {
+ { false, new string[0], true },
+ { true, new string[0], true },
+
+ { false, new string[] { "application/xml" }, true },
+ { true, new string[] { "application/xml" }, false },
+
+ { false, new string[] { "application/xml; q=1.0" }, true },
+ { true, new string[] { "application/xml; q=1.0" }, false },
+
+ { false, new string[] { "application/xml; q=0.0" }, true },
+ { true, new string[] { "application/xml; q=0.0" }, false },
+
+ { false, new string[] { "application/xml; q=0.0", "application/json" }, true },
+ { true, new string[] { "application/xml; q=0.0", "application/json" }, false },
+
+ { false, new string[] { "text/nomatch" }, true },
+ { true, new string[] { "text/nomatch" }, false },
+ };
+ }
+ }
+
+ public static TheoryData MatchTypeData
+ {
+ get
+ {
+ // string[] supportedMediaTypes, string expectedMediaType
+ return new TheoryData
+ {
+ { new string[0], "application/octet-stream" },
+
+ { new string[] { "text/xml", "application/xml" }, "text/xml" },
+ { new string[] { "application/xml", "text/xml" }, "application/xml" },
+ };
+ }
+ }
+
+ public static TheoryData SelectResponseCharacterEncodingData
+ {
+ get
+ {
+ // string[] acceptEncodings, string requestEncoding, string[] supportedEncodings, string expectedEncoding
+ return new TheoryData
+ {
+ { new string[] { "utf-8" }, null, new string[0], null },
+ { new string[0], "utf-8", new string[0], null },
+
+ { new string[0], null, new string[] { "utf-8", "utf-16"}, "utf-8" },
+ { new string[0], "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
+
+ { new string[] { "utf-8" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
+ { new string[] { "utf-16" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" },
+ { new string[] { "utf-16; q=0.5" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" },
+
+ { new string[] { "utf-8; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
+ { new string[] { "utf-8; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
+ { new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
+ { new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
+ { new string[] { "*; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
+ { new string[] { "*; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
+ };
+ }
+ }
+
+ public static TheoryData, MediaTypeFormatterMatch> SelectResponseMediaTypeData
+ {
+ get
+ {
+#if !ASPNETCORE50
+ // Only mapping and accept makes sense with q != 1.0
+ MediaTypeFormatterMatch matchMapping10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping);
+ MediaTypeFormatterMatch matchMapping05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping);
+#endif
+
+ MediaTypeFormatterMatch matchAccept10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral);
+ MediaTypeFormatterMatch matchAccept05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral);
+
+ MediaTypeFormatterMatch matchAcceptSubTypeRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange);
+ MediaTypeFormatterMatch matchAcceptSubTypeRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange);
+
+ MediaTypeFormatterMatch matchAcceptAllRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange);
+ MediaTypeFormatterMatch matchAcceptAllRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange);
+
+ MediaTypeFormatterMatch matchRequest10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestMediaType);
+ MediaTypeFormatterMatch matchType10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnCanWriteType);
+
+ // ICollection candidateMatches, MediaTypeFormatterMatch winner
+ return new TheoryData, MediaTypeFormatterMatch>
+ {
+ { new List(), null },
+ { new List() { matchType10 }, matchType10 },
+ { new List() { matchType10, matchRequest10 }, matchRequest10 },
+ { new List() { matchType10, matchRequest10, matchAcceptAllRange10 }, matchAcceptAllRange10 },
+ { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 },
+ { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10, matchAccept10 }, matchAccept10 },
+#if !ASPNETCORE50
+ { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10, matchAccept10, matchMapping10 }, matchMapping10 },
+#endif
+ { new List() { matchAccept05, matchAccept10 }, matchAccept10 },
+ { new List() { matchAccept10, matchAccept05 }, matchAccept10 },
+
+ { new List() { matchAcceptSubTypeRange05, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 },
+ { new List() { matchAcceptSubTypeRange10, matchAcceptSubTypeRange05 }, matchAcceptSubTypeRange10 },
+
+ { new List() { matchAcceptAllRange05, matchAcceptAllRange10 }, matchAcceptAllRange10 },
+ { new List() { matchAcceptAllRange10, matchAcceptAllRange05 }, matchAcceptAllRange10 },
+#if !ASPNETCORE50
+ { new List() { matchMapping05, matchMapping10 }, matchMapping10 },
+ { new List() { matchMapping10, matchMapping05 }, matchMapping10 },
+
+ { new List() { matchMapping05, matchAccept05 }, matchMapping05 },
+ { new List() { matchMapping10, matchAccept10 }, matchMapping10 },
+
+ { new List() { matchMapping05, matchAcceptSubTypeRange05 }, matchMapping05 },
+ { new List() { matchMapping10, matchAcceptSubTypeRange10 }, matchMapping10 },
+
+ { new List() { matchMapping05, matchAcceptAllRange05 }, matchMapping05 },
+ { new List() { matchMapping10, matchAcceptAllRange10 }, matchMapping10 },
+
+ { new List() { matchMapping05, matchAccept10 }, matchAccept10 },
+ { new List() { matchMapping05, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 },
+ { new List() { matchMapping05, matchAcceptAllRange10 }, matchAcceptAllRange10 },
+#endif
+ };
+ }
+ }
+
+ public static TheoryData UpdateBestMatchData
+ {
+ get
+ {
+ MediaTypeFormatterMatch matchMapping10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.None);
+ MediaTypeFormatterMatch matchMapping05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.None);
+
+ // MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement, currentWins
+ return new TheoryData
+ {
+ { null, matchMapping10, false },
+ { null, matchMapping05, false },
+
+ { matchMapping10, matchMapping10, true },
+ { matchMapping10, matchMapping05, true },
+
+ { matchMapping05, matchMapping10, false },
+ { matchMapping05, matchMapping05, true },
+ };
+ }
+ }
+
+ private static MediaTypeFormatterMatch CreateMatch(double? quality, MediaTypeFormatterMatchRanking ranking)
+ {
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
+ MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("text/test");
+ return new MediaTypeFormatterMatch(formatter, mediaType, quality, ranking);
+ }
+
+ [Fact]
+ public void TypeIsCorrect()
+ {
+ new TypeAssert().HasProperties(typeof(DefaultContentNegotiator), TypeAssert.TypeProperties.IsPublicVisibleClass);
+ }
+
+ [Fact]
+ public void Negotiate_ForEmptyFormatterCollection_ReturnsNull()
+ {
+ var result = _negotiator.Negotiate(typeof(string), _request, Enumerable.Empty());
+
+ Assert.Null(result);
+ }
+
+#if !ASPNETCORE50
+
+ [Fact]
+ public void Negotiate_MediaTypeMappingTakesPrecedenceOverAcceptHeader()
+ {
+ // Prepare the request message
+ _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
+ _request.Headers.Add("Browser", "IE");
+ _request.Headers.Add("Cookie", "ABC");
+
+ // Prepare the formatters
+ List formatters = new List();
+ formatters.Add(new JsonMediaTypeFormatter());
+ formatters.Add(new XmlMediaTypeFormatter());
+ PlainTextFormatter frmtr = new PlainTextFormatter();
+ frmtr.SupportedMediaTypes.Clear();
+ frmtr.MediaTypeMappings.Clear();
+ frmtr.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
+ frmtr.MediaTypeMappings.Add(new MyMediaTypeMapping(new MediaTypeHeaderValue(("application/xml"))));
+ formatters.Add(frmtr);
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, formatters);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal("application/xml", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+#endif
+
+ [Fact]
+ public void Negotiate_ForRequestReturnsFirstMatchingFormatter()
+ {
+ MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/myMediaType");
+
+ MediaTypeFormatter formatter1 = new MockMediaTypeFormatter()
+ {
+ CanWriteTypeCallback = (Type t) => false
+ };
+
+ MediaTypeFormatter formatter2 = new MockMediaTypeFormatter()
+ {
+ CanWriteTypeCallback = (Type t) => true
+ };
+
+ formatter2.SupportedMediaTypes.Add(mediaType);
+
+ MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(
+ new MediaTypeFormatter[]
+ {
+ formatter1,
+ formatter2
+ });
+
+ _request.Content = new StringContent("test", Encoding.UTF8, mediaType.MediaType);
+
+ var result = _negotiator.Negotiate(typeof(string), _request, collection);
+ Assert.Same(formatter2, result.Formatter);
+ new MediaTypeAssert().AreEqual(mediaType, result.MediaType, "Expected the formatter's media type to be returned.");
+ }
+
+ [Fact]
+ public void Negotiate_SelectsJsonAsDefaultFormatter()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ Assert.IsType(result.Formatter);
+ Assert.Equal(MediaTypeConstants.ApplicationJsonMediaType.MediaType, result.MediaType.MediaType);
+ }
+
+ [Fact]
+ public void Negotiate_SelectsXmlFormatter_ForXhrRequestThatAcceptsXml()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+ _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ Assert.Equal("application/xml", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+ [Fact]
+ public void Negotiate_SelectsJsonFormatter_ForXhrRequestThatDoesNotSpecifyAcceptHeaders()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ Assert.Equal("application/json", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+#if !ASPNETCORE50
+
+ [Fact]
+ public void Negotiate_RespectsFormatterOrdering_ForXhrRequestThatDoesNotSpecifyAcceptHeaders()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+
+ MediaTypeFormatterCollection formatters = new MediaTypeFormatterCollection(new MediaTypeFormatter[]
+ {
+ new XmlMediaTypeFormatter(),
+ new JsonMediaTypeFormatter(),
+ new FormUrlEncodedMediaTypeFormatter()
+ });
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, formatters);
+
+ // Assert
+ Assert.Equal("application/json", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+#endif
+
+ [Fact]
+ public void Negotiate_SelectsJsonFormatter_ForXHRAndJsonValueResponse()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(JToken), _request, new MediaTypeFormatterCollection());
+
+ Assert.Equal("application/json", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+ [Fact]
+ public void Negotiate_SelectsJsonFormatter_ForXHRAndMatchAllAcceptHeader()
+ {
+ // Accept
+ _request.Content = new StringContent("test");
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+ _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ Assert.Equal("application/json", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+ [Fact]
+ public void Negotiate_UsesRequestedFormatterForXHRAndMatchAllPlusOtherAcceptHeader()
+ {
+ // Arrange
+ _request.Content = new StringContent("test");
+ _request.Headers.Add("x-requested-with", "XMLHttpRequest");
+ _request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); // XHR header sent by Firefox 3b5
+
+ // Act
+ var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ Assert.Equal("application/xml", result.MediaType.MediaType);
+ Assert.IsType(result.Formatter);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void Negotiate_ObservesExcludeMatchOnTypeOnly(bool excludeMatchOnTypeOnly)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnTypeOnly);
+ _request.Content = new StringContent("test");
+ _request.Headers.Accept.ParseAdd("text/html");
+
+ // Act
+ var result = negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
+
+ // Assert
+ if (excludeMatchOnTypeOnly)
+ {
+ Assert.Null(result);
+ }
+ else
+ {
+ Assert.NotNull(result);
+ Assert.Equal("application/json", result.MediaType.MediaType);
+ }
+ }
+
+#if !ASPNETCORE50
+
+ [Fact]
+ public void MatchMediaTypeMapping_ReturnsMatch()
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ HttpRequestMessage request = new HttpRequestMessage();
+ MediaTypeHeaderValue mappingMediatype = MediaTypeHeaderValue.Parse("application/other");
+ MockMediaTypeMapping mockMediaTypeMapping = new MockMediaTypeMapping(mappingMediatype, 0.75);
+
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
+ formatter.MediaTypeMappings.Add(mockMediaTypeMapping);
+
+ // Act
+ MediaTypeFormatterMatch match = negotiator.MatchMediaTypeMapping(request, formatter);
+
+ // Assert
+ Assert.True(mockMediaTypeMapping.WasInvoked);
+ Assert.Same(request, mockMediaTypeMapping.Request);
+
+ Assert.Same(formatter, match.Formatter);
+ Assert.Equal(mockMediaTypeMapping.MediaType, match.MediaType);
+ Assert.Equal(mockMediaTypeMapping.MatchQuality, match.Quality);
+ Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping, match.Ranking);
+ }
+
+#endif
+
+ [Theory]
+ [MemberData("MatchAcceptHeaderData")]
+ public void MatchAcceptHeader_ReturnsMatch(string[] acceptHeaders, string[] supportedMediaTypes, string expectedMediaType, double expectedQuality, int ranking)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ List unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList();
+ IEnumerable sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders);
+
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
+ foreach (string supportedMediaType in supportedMediaTypes)
+ {
+ formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
+ }
+
+ // Act
+ MediaTypeFormatterMatch match = negotiator.MatchAcceptHeader(sortedAcceptHeaders, formatter);
+
+ // Assert
+ if (expectedMediaType == null)
+ {
+ Assert.Null(match);
+ }
+ else
+ {
+ Assert.Same(formatter, match.Formatter);
+ Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
+ Assert.Equal(expectedQuality, match.Quality);
+ Assert.Equal(ranking, (int)match.Ranking);
+ }
+ }
+
+ [Theory]
+ [MemberData("MatchRequestMediaTypeData")]
+ public void MatchRequestMediaType_ReturnsMatch(string requestMediaType, string[] supportedMediaTypes, string expectedMediaType)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ HttpRequestMessage request = new HttpRequestMessage();
+ request.Content = new StringContent(String.Empty);
+ request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(requestMediaType);
+
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
+ foreach (string supportedMediaType in supportedMediaTypes)
+ {
+ formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
+ }
+
+ // Act
+ MediaTypeFormatterMatch match = negotiator.MatchRequestMediaType(request, formatter);
+
+ // Assert
+ if (expectedMediaType == null)
+ {
+ Assert.Null(match);
+ }
+ else
+ {
+ Assert.Same(formatter, match.Formatter);
+ Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
+ Assert.Equal(1.0, match.Quality);
+ Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnRequestMediaType, match.Ranking);
+ }
+ }
+
+ [Theory]
+ [MemberData("ShouldMatchOnTypeData")]
+ public void ShouldMatchOnType_ReturnsExpectedResult(bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnType);
+ List unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList();
+ IEnumerable sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders);
+
+ // Act
+ bool result = negotiator.ShouldMatchOnType(sortedAcceptHeaders);
+
+ // Assert
+ Assert.Equal(expectedResult, result);
+ }
+
+ [Theory]
+ [MemberData("MatchTypeData")]
+ public void MatchType_ReturnsMatch(string[] supportedMediaTypes, string expectedMediaType)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
+ foreach (string supportedMediaType in supportedMediaTypes)
+ {
+ formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
+ }
+
+ // Act
+ MediaTypeFormatterMatch match = negotiator.MatchType(typeof(object), formatter);
+
+ // Assert
+ Assert.Same(formatter, match.Formatter);
+ Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
+ Assert.Equal(1.0, match.Quality);
+ Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnCanWriteType, match.Ranking);
+ }
+
+ [Theory]
+ [MemberData("SelectResponseMediaTypeData")]
+ public void SelectResponseMediaTypeFormatter_SelectsMediaType(ICollection matches, MediaTypeFormatterMatch expectedWinner)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ // Act
+ MediaTypeFormatterMatch actualWinner = negotiator.SelectResponseMediaTypeFormatter(matches);
+
+ // Assert
+ Assert.Same(expectedWinner, actualWinner);
+ }
+
+ [Theory]
+ [MemberData("SelectResponseCharacterEncodingData")]
+ public void SelectResponseCharacterEncoding_SelectsEncoding(string[] acceptCharsetHeaders, string requestEncoding, string[] supportedEncodings, string expectedEncoding)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ HttpRequestMessage request = new HttpRequestMessage();
+ foreach (string acceptCharsetHeader in acceptCharsetHeaders)
+ {
+ request.Headers.AcceptCharset.Add(StringWithQualityHeaderValue.Parse(acceptCharsetHeader));
+ }
+
+ if (requestEncoding != null)
+ {
+ Encoding reqEncoding = Encoding.GetEncoding(requestEncoding);
+ StringContent content = new StringContent("", reqEncoding, "text/plain");
+ request.Content = content;
+ }
+
+ MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true };
+ foreach (string supportedEncoding in supportedEncodings)
+ {
+ formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding));
+ }
+
+ // Act
+ Encoding actualEncoding = negotiator.SelectResponseCharacterEncoding(request, formatter);
+
+ // Assert
+ if (expectedEncoding == null)
+ {
+ Assert.Null(actualEncoding);
+ }
+ else
+ {
+ Assert.Equal(Encoding.GetEncoding(expectedEncoding), actualEncoding);
+ }
+ }
+
+ [Theory]
+ [TestDataSet(typeof(DefaultContentNegotiatorTests), nameof(MediaTypeWithQualityHeaderValueComparerTestsBeforeAfterSortedValues))]
+ public void SortMediaTypeWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable unsorted, IEnumerable expectedSorted)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ List unsortedValues =
+ new List(unsorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)));
+
+ List expectedSortedValues =
+ new List(expectedSorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)));
+
+ // Act
+ IEnumerable actualSorted = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedValues);
+
+ // Assert
+ Assert.True(expectedSortedValues.SequenceEqual(actualSorted));
+ }
+
+ public static TheoryData MediaTypeWithQualityHeaderValueComparerTestsBeforeAfterSortedValues
+ {
+ get
+ {
+ return new TheoryData
+ {
+ {
+ new string[]
+ {
+ "application/*",
+ "text/plain",
+ "text/plain;q=1.0",
+ "text/plain",
+ "text/plain;q=0",
+ "*/*;q=0.8",
+ "*/*;q=1",
+ "text/*;q=1",
+ "text/plain;q=0.8",
+ "text/*;q=0.8",
+ "text/*;q=0.6",
+ "text/*;q=1.0",
+ "*/*;q=0.4",
+ "text/plain;q=0.6",
+ "text/xml",
+ },
+ new string[]
+ {
+ "text/plain",
+ "text/plain;q=1.0",
+ "text/plain",
+ "text/xml",
+ "application/*",
+ "text/*;q=1",
+ "text/*;q=1.0",
+ "*/*;q=1",
+ "text/plain;q=0.8",
+ "text/*;q=0.8",
+ "*/*;q=0.8",
+ "text/plain;q=0.6",
+ "text/*;q=0.6",
+ "*/*;q=0.4",
+ "text/plain;q=0",
+ }
+ }
+ };
+ }
+ }
+
+ [Theory]
+ [TestDataSet(typeof(DefaultContentNegotiatorTests), nameof(StringWithQualityHeaderValueComparerTestsBeforeAfterSortedValues))]
+ public void SortStringWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable unsorted, IEnumerable expectedSorted)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ List unsortedValues =
+ new List(unsorted.Select(u => StringWithQualityHeaderValue.Parse(u)));
+
+ List expectedSortedValues =
+ new List(expectedSorted.Select(u => StringWithQualityHeaderValue.Parse(u)));
+
+ // Act
+ IEnumerable actualSorted = negotiator.SortStringWithQualityHeaderValuesByQFactor(unsortedValues);
+
+ // Assert
+ Assert.True(expectedSortedValues.SequenceEqual(actualSorted));
+ }
+
+ public static TheoryData StringWithQualityHeaderValueComparerTestsBeforeAfterSortedValues
+ {
+ get
+ {
+ return new TheoryData
+ {
+ {
+ new string[]
+ {
+ "text",
+ "text;q=1.0",
+ "text",
+ "text;q=0",
+ "*;q=0.8",
+ "*;q=1",
+ "text;q=0.8",
+ "*;q=0.6",
+ "text;q=1.0",
+ "*;q=0.4",
+ "text;q=0.6",
+ },
+ new string[]
+ {
+ "text",
+ "text;q=1.0",
+ "text",
+ "text;q=1.0",
+ "*;q=1",
+ "text;q=0.8",
+ "*;q=0.8",
+ "text;q=0.6",
+ "*;q=0.6",
+ "*;q=0.4",
+ "text;q=0",
+ }
+ }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData("UpdateBestMatchData")]
+ public void UpdateBestMatch_SelectsCorrectly(MediaTypeFormatterMatch current, MediaTypeFormatterMatch replacement, bool currentWins)
+ {
+ // Arrange
+ MockContentNegotiator negotiator = new MockContentNegotiator();
+
+ // Act
+ MediaTypeFormatterMatch actualResult = negotiator.UpdateBestMatch(current, replacement);
+
+ // Assert
+ if (currentWins)
+ {
+ Assert.Same(current, actualResult);
+ }
+ else
+ {
+ Assert.Same(replacement, actualResult);
+ }
+ }
+
+ private class PlainTextFormatter : MediaTypeFormatter
+ {
+ public override bool CanReadType(Type type)
+ {
+ return true;
+ }
+
+ public override bool CanWriteType(Type type)
+ {
+ return true;
+ }
+ }
+
+#if !ASPNETCORE50
+
+ private class MyMediaTypeMapping : MediaTypeMapping
+ {
+ public MyMediaTypeMapping(MediaTypeHeaderValue mediaType)
+ : base(mediaType)
+ {
+ }
+
+ public override double TryMatchMediaType(HttpRequestMessage request)
+ {
+ if (request.Headers.Contains("Cookie"))
+ {
+ return 1.0;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+#endif
+
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs
index 3a30640193..74a25ead29 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs
@@ -108,6 +108,8 @@ namespace System.Web.Http.Dispatcher
Assert.DoesNotContain("OH NO", modelStateError["[2].Name"] as IEnumerable);
}
+#if !ASPNETCORE50
+
[Fact]
public void HttpError_Roundtrips_WithJsonFormatter()
{
@@ -196,6 +198,8 @@ namespace System.Web.Http.Dispatcher
serializedError);
}
+#endif
+
[Fact]
public void GetPropertyValue_GetsValue_IfTypeMatches()
{
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpRequestMessage/HttpRequestMessageExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpRequestMessage/HttpRequestMessageExtensionsTest.cs
index 0e4d71c009..37ea900b0a 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpRequestMessage/HttpRequestMessageExtensionsTest.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpRequestMessage/HttpRequestMessageExtensionsTest.cs
@@ -8,13 +8,46 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.AspNet.PipelineCore;
using Microsoft.Framework.OptionsModel;
+#if !ASPNETCORE50
using Moq;
+#endif
using Xunit;
namespace System.Net.Http
{
public class HttpRequestMessageExtensionsTest
{
+ [Fact]
+ public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsInvalidFormat_Throws()
+ {
+ HttpRequestMessage request = CreateRequest(new DefaultHttpContext());
+
+ var ex = Assert.Throws(
+ () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), "foo/bar; param=value"));
+
+ Assert.Equal("The format of value 'foo/bar; param=value' is invalid.", ex.Message);
+ }
+
+ [Fact]
+ public void CreateResponse_MatchingMediaType_WhenRequestDoesNotHaveHttpContextThrows()
+ {
+ HttpRequestMessage request = CreateRequest(null);
+
+ // Arrange
+
+ // Act
+ var ex = Assert.Throws(
+ () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar"));
+
+ Assert.Equal(
+ "The HttpRequestMessage instance is not properly initialized. " +
+ "Use HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage to create an HttpRequestMessage " +
+ "for the current request.",
+ ex.Message);
+ }
+
+#if !ASPNETCORE50
+
[Fact]
public void CreateResponse_DoingConneg_OnlyContent_RetrievesContentNegotiatorFromServices()
{
@@ -110,36 +143,6 @@ namespace System.Net.Http
Assert.Same(formatter, objectContent.Formatter);
}
-
- [Fact]
- public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsInvalidFormat_Throws()
- {
- HttpRequestMessage request = CreateRequest(new DefaultHttpContext());
-
- var ex = Assert.Throws(
- () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), "foo/bar; param=value"));
-
- Assert.Equal("The format of value 'foo/bar; param=value' is invalid.", ex.Message);
- }
-
- [Fact]
- public void CreateResponse_MatchingMediaType_WhenRequestDoesNotHaveHttpContextThrows()
- {
- HttpRequestMessage request = CreateRequest(null);
-
- // Arrange
-
- // Act
- var ex = Assert.Throws(
- () => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar"));
-
- Assert.Equal(
- "The HttpRequestMessage instance is not properly initialized. " +
- "Use HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage to create an HttpRequestMessage " +
- "for the current request.",
- ex.Message);
- }
-
[Fact]
public void CreateResponse_MatchingMediaType_WhenMediaTypeDoesNotMatch_Throws()
{
@@ -299,20 +302,8 @@ namespace System.Net.Http
Assert.Same(expectedContentRange, response.Content.Headers.ContentRange);
}
- private static HttpRequestMessage CreateRequest(HttpContext context)
- {
- var request = new HttpRequestMessage();
- request.Properties.Add(nameof(HttpContext), context);
- return request;
- }
-
- private static object CreateValue()
- {
- return new object();
- }
-
private static IServiceProvider CreateServices(
- IContentNegotiator contentNegotiator = null,
+ IContentNegotiator contentNegotiator = null,
MediaTypeFormatter formatter = null)
{
var options = new WebApiCompatShimOptions();
@@ -343,5 +334,17 @@ namespace System.Net.Http
return services.Object;
}
+#endif
+ private static object CreateValue()
+ {
+ return new object();
+ }
+
+ private static HttpRequestMessage CreateRequest(HttpContext context)
+ {
+ var request = new HttpRequestMessage();
+ request.Properties.Add(nameof(HttpContext), context);
+ return request;
+ }
}
}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseExceptionActionFilterTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseExceptionActionFilterTest.cs
index b03db244ef..0fc6689f64 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseExceptionActionFilterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseExceptionActionFilterTest.cs
@@ -6,13 +6,28 @@ using System.Net;
using System.Net.Http;
using Microsoft.AspNet.PipelineCore;
using Microsoft.AspNet.Routing;
+#if !ASPNETCORE50
using Moq;
+#endif
using Xunit;
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
{
public class HttpResponseExceptionActionFilterTest
{
+ [Fact]
+ public void OrderIsSetToMaxValue()
+ {
+ // Arrange
+ var filter = new HttpResponseExceptionActionFilter();
+ var expectedFilterOrder = int.MaxValue - 10;
+
+ // Act & Assert
+ Assert.Equal(expectedFilterOrder, filter.Order);
+ }
+
+#if !ASPNETCORE50
+
[Fact]
public void OnActionExecuting_IsNoOp()
{
@@ -32,17 +47,6 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
Assert.Null(context.Result);
}
- [Fact]
- public void OrderIsSetToMaxValue()
- {
- // Arrange
- var filter = new HttpResponseExceptionActionFilter();
- var expectedFilterOrder = int.MaxValue - 10;
-
- // Act & Assert
- Assert.Equal(expectedFilterOrder, filter.Order);
- }
-
[Fact]
public void OnActionExecuted_HandlesExceptionAndReturnsObjectResult()
{
@@ -71,5 +75,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
Assert.Equal(context.HttpContext.GetHttpRequestMessage(), response.RequestMessage);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
+
+#endif
+
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseMessageOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseMessageOutputFormatterTests.cs
index 4c241ebf14..c90664d31c 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseMessageOutputFormatterTests.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpResponseMessageOutputFormatterTests.cs
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+#if !ASPNETCORE50
+
using System;
using System.IO;
using System.Net.Http;
@@ -40,7 +42,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShimTest
Object = outputValue,
DeclaredType = outputType,
ActionContext = new ActionContext(new DefaultHttpContext(), routeData: null, actionDescriptor: null)
- };
+ };
}
}
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockContentNegotiator.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockContentNegotiator.cs
new file mode 100644
index 0000000000..a959c59e75
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockContentNegotiator.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Net.Http.Headers;
+using System.Text;
+
+namespace System.Net.Http.Formatting.Mocks
+{
+ public class MockContentNegotiator : DefaultContentNegotiator
+ {
+ public MockContentNegotiator()
+ {
+ }
+
+ public MockContentNegotiator(bool excludeMatchOnTypeOnly)
+ : base(excludeMatchOnTypeOnly)
+ {
+ }
+
+ public new Collection ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable formatters)
+ {
+ return base.ComputeFormatterMatches(type, request, formatters);
+ }
+
+ public new MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection matches)
+ {
+ return base.SelectResponseMediaTypeFormatter(matches);
+ }
+
+ public new Encoding SelectResponseCharacterEncoding(HttpRequestMessage request, MediaTypeFormatter formatter)
+ {
+ return base.SelectResponseCharacterEncoding(request, formatter);
+ }
+
+#if !ASPNETCORE50
+
+ public new MediaTypeFormatterMatch MatchMediaTypeMapping(HttpRequestMessage request, MediaTypeFormatter formatter)
+ {
+ return base.MatchMediaTypeMapping(request, formatter);
+ }
+
+#endif
+
+ public new MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable sortedAcceptValues, MediaTypeFormatter formatter)
+ {
+ return base.MatchAcceptHeader(sortedAcceptValues, formatter);
+ }
+
+ public new MediaTypeFormatterMatch MatchRequestMediaType(HttpRequestMessage request, MediaTypeFormatter formatter)
+ {
+ return base.MatchRequestMediaType(request, formatter);
+ }
+
+ public new bool ShouldMatchOnType(IEnumerable sortedAcceptValues)
+ {
+ return base.ShouldMatchOnType(sortedAcceptValues);
+ }
+
+ public new MediaTypeFormatterMatch MatchType(Type type, MediaTypeFormatter formatter)
+ {
+ return base.MatchType(type, formatter);
+ }
+
+ public new IEnumerable SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection headerValues)
+ {
+ return base.SortMediaTypeWithQualityHeaderValuesByQFactor(headerValues);
+ }
+
+ public new IEnumerable SortStringWithQualityHeaderValuesByQFactor(ICollection headerValues)
+ {
+ return base.SortStringWithQualityHeaderValuesByQFactor(headerValues);
+ }
+
+ public new MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement)
+ {
+ return base.UpdateBestMatch(current, potentialReplacement);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeFormatter.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeFormatter.cs
new file mode 100644
index 0000000000..6fa5c9f5f4
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeFormatter.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Net.Http.Headers;
+using System.Text;
+
+namespace System.Net.Http.Formatting.Mocks
+{
+ public class MockMediaTypeFormatter : MediaTypeFormatter
+ {
+ private bool _canWriteAnyTypes = true;
+ public bool CallBase { get; set; }
+ public Func CanReadTypeCallback { get; set; }
+ public Func CanWriteTypeCallback { get; set; }
+
+ public bool CanWriteAnyTypesReturn
+ {
+ get { return _canWriteAnyTypes; }
+ set { _canWriteAnyTypes = value; }
+ }
+
+ public override bool CanReadType(Type type)
+ {
+ if (!CallBase && CanReadTypeCallback == null)
+ {
+ throw new InvalidOperationException("CallBase or CanReadTypeCallback must be set first.");
+ }
+
+ return CanReadTypeCallback != null ? CanReadTypeCallback(type) : true;
+ }
+
+ public override bool CanWriteType(Type type)
+ {
+ if (!CallBase && CanWriteTypeCallback == null)
+ {
+ throw new InvalidOperationException("CallBase or CanWriteTypeCallback must be set first.");
+ }
+
+ return CanWriteTypeCallback != null ? CanWriteTypeCallback(type) : true;
+ }
+
+ public new Encoding SelectCharacterEncoding(HttpContentHeaders contentHeaders)
+ {
+ return base.SelectCharacterEncoding(contentHeaders);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeMapping.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeMapping.cs
new file mode 100644
index 0000000000..7fde707f3e
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/Mocks/MockMediaTypeMapping.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#if !ASPNETCORE50
+
+using System.Net.Http.Headers;
+namespace System.Net.Http.Formatting.Mocks
+{
+ public class MockMediaTypeMapping : MediaTypeMapping
+ {
+ public MockMediaTypeMapping(string mediaType, double matchQuality)
+ : base(mediaType)
+ {
+ MatchQuality = matchQuality;
+ }
+
+ public MockMediaTypeMapping(MediaTypeHeaderValue mediaType, double matchQuality)
+ : base(mediaType)
+ {
+ MatchQuality = matchQuality;
+ }
+
+ public double MatchQuality { get; private set; }
+
+ public HttpRequestMessage Request { get; private set; }
+
+ public bool WasInvoked { get; private set; }
+
+ public override double TryMatchMediaType(HttpRequestMessage request)
+ {
+ WasInvoked = true;
+ Request = request;
+ return MatchQuality;
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/FlagsEnum.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/FlagsEnum.cs
new file mode 100644
index 0000000000..ad24d873e0
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/FlagsEnum.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.TestCommon.Types
+{
+ [Flags]
+ public enum FlagsEnum
+ {
+ One = 0x1,
+ Two = 0x2,
+ Four = 0x4
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/LongEnum.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/LongEnum.cs
new file mode 100644
index 0000000000..05a22e7cae
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/LongEnum.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.TestCommon.Types
+{
+ public enum LongEnum : long
+ {
+ FirstLong,
+ SecondLong,
+ ThirdLong,
+ FourthLong
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeAssert.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeAssert.cs
new file mode 100644
index 0000000000..1e414e57e1
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeAssert.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Net.Http.Headers;
+using Xunit;
+using Microsoft.AspNet.Mvc;
+
+namespace Microsoft.TestCommon
+{
+ public class MediaTypeAssert
+ {
+ private static readonly MediaTypeAssert singleton = new MediaTypeAssert();
+
+ public static MediaTypeAssert Singleton { get { return singleton; } }
+
+ public void AreEqual(MediaTypeHeaderValue expected, MediaTypeHeaderValue actual, string errorMessage)
+ {
+ if (expected != null || actual != null)
+ {
+ Assert.NotNull(expected);
+ Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actual));
+ }
+ }
+
+ public void AreEqual(MediaTypeHeaderValue expected, string actual, string errorMessage)
+ {
+ if (expected != null || !String.IsNullOrEmpty(actual))
+ {
+ MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual);
+ Assert.NotNull(expected);
+ Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actualMediaType));
+ }
+ }
+
+ public void AreEqual(string expected, string actual, string errorMessage)
+ {
+ if (!String.IsNullOrEmpty(expected) || !String.IsNullOrEmpty(actual))
+ {
+ Assert.NotNull(expected);
+ MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected);
+ MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual);
+ Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actualMediaType));
+ }
+ }
+
+ public void AreEqual(string expected, MediaTypeHeaderValue actual, string errorMessage)
+ {
+ if (!String.IsNullOrEmpty(expected) || actual != null)
+ {
+ Assert.NotNull(expected);
+ MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected);
+ Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actual));
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeConstants.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeConstants.cs
new file mode 100644
index 0000000000..191406571b
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeConstants.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Net.Http.Headers;
+
+namespace System.Net.Http.Formatting
+{
+ ///
+ /// Constants related to media types.
+ ///
+ internal static class MediaTypeConstants
+ {
+ private static readonly MediaTypeHeaderValue _defaultApplicationXmlMediaType = new MediaTypeHeaderValue("application/xml");
+ private static readonly MediaTypeHeaderValue _defaultTextXmlMediaType = new MediaTypeHeaderValue("text/xml");
+ private static readonly MediaTypeHeaderValue _defaultApplicationJsonMediaType = new MediaTypeHeaderValue("application/json");
+ private static readonly MediaTypeHeaderValue _defaultTextJsonMediaType = new MediaTypeHeaderValue("text/json");
+ private static readonly MediaTypeHeaderValue _defaultApplicationOctetStreamMediaType = new MediaTypeHeaderValue("application/octet-stream");
+ private static readonly MediaTypeHeaderValue _defaultApplicationFormUrlEncodedMediaType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
+ private static readonly MediaTypeHeaderValue _defaultApplicationBsonMediaType = new MediaTypeHeaderValue("application/bson");
+
+ ///
+ /// Gets a instance representing application/octet-stream.
+ ///
+ ///
+ /// A new instance representing application/octet-stream.
+ ///
+ public static MediaTypeHeaderValue ApplicationOctetStreamMediaType
+ {
+ get { return _defaultApplicationOctetStreamMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/xml.
+ ///
+ ///
+ /// A new instance representing application/xml.
+ ///
+ public static MediaTypeHeaderValue ApplicationXmlMediaType
+ {
+ get { return _defaultApplicationXmlMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/json.
+ ///
+ ///
+ /// A new instance representing application/json.
+ ///
+ public static MediaTypeHeaderValue ApplicationJsonMediaType
+ {
+ get { return _defaultApplicationJsonMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing text/xml.
+ ///
+ ///
+ /// A new instance representing text/xml.
+ ///
+ public static MediaTypeHeaderValue TextXmlMediaType
+ {
+ get { return _defaultTextXmlMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing text/json.
+ ///
+ ///
+ /// A new instance representing text/json.
+ ///
+ public static MediaTypeHeaderValue TextJsonMediaType
+ {
+ get { return _defaultTextJsonMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/x-www-form-urlencoded.
+ ///
+ ///
+ /// A new instance representing application/x-www-form-urlencoded.
+ ///
+ public static MediaTypeHeaderValue ApplicationFormUrlEncodedMediaType
+ {
+ get { return _defaultApplicationFormUrlEncodedMediaType; }
+ }
+
+ ///
+ /// Gets a instance representing application/bson.
+ ///
+ ///
+ /// A new instance representing application/bson.
+ ///
+ ///
+ /// Not yet a standard. In particular this media type is not currently listed at
+ /// http://www.iana.org/assignments/media-types/application.
+ ///
+ public static MediaTypeHeaderValue ApplicationBsonMediaType
+ {
+ get { return _defaultApplicationBsonMediaType; }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeHeaderValueComparer.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeHeaderValueComparer.cs
new file mode 100644
index 0000000000..89edc53905
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/MediaTypeHeaderValueComparer.cs
@@ -0,0 +1,195 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http.Headers;
+
+namespace Microsoft.TestCommon
+{
+ public class MediaTypeHeaderValueComparer : IComparer
+ {
+ private static readonly MediaTypeHeaderValueComparer mediaTypeComparer = new MediaTypeHeaderValueComparer();
+
+ public MediaTypeHeaderValueComparer()
+ {
+ }
+
+ public static MediaTypeHeaderValueComparer Comparer
+ {
+ get
+ {
+ return mediaTypeComparer;
+ }
+ }
+
+ public int Compare(MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2)
+ {
+ ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1);
+ ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2);
+
+ int returnValue = CompareBasedOnQualityFactor(parsedMediaType1, parsedMediaType2);
+
+ if (returnValue == 0)
+ {
+ if (!String.Equals(parsedMediaType1.Type, parsedMediaType2.Type, StringComparison.OrdinalIgnoreCase))
+ {
+ if (parsedMediaType1.IsAllMediaRange)
+ {
+ return 1;
+ }
+ else if (parsedMediaType2.IsAllMediaRange)
+ {
+ return -1;
+ }
+ }
+ else if (!String.Equals(parsedMediaType1.SubType, parsedMediaType2.SubType, StringComparison.OrdinalIgnoreCase))
+ {
+ if (parsedMediaType1.IsSubTypeMediaRange)
+ {
+ return 1;
+ }
+ else if (parsedMediaType2.IsSubTypeMediaRange)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (!parsedMediaType1.HasNonQualityFactorParameter)
+ {
+ if (parsedMediaType2.HasNonQualityFactorParameter)
+ {
+ return 1;
+ }
+ }
+ else if (!parsedMediaType2.HasNonQualityFactorParameter)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return returnValue;
+ }
+
+ private static int CompareBasedOnQualityFactor(ParsedMediaTypeHeaderValue parsedMediaType1, ParsedMediaTypeHeaderValue parsedMediaType2)
+ {
+ double qualityDifference = parsedMediaType1.QualityFactor - parsedMediaType2.QualityFactor;
+ if (qualityDifference < 0)
+ {
+ return 1;
+ }
+ else if (qualityDifference > 0)
+ {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ internal class ParsedMediaTypeHeaderValue
+ {
+ private const string MediaRangeAsterisk = "*";
+ private const char MediaTypeSubTypeDelimiter = '/';
+ private const string QualityFactorParameterName = "q";
+ private const double DefaultQualityFactor = 1.0;
+
+ private MediaTypeHeaderValue mediaType;
+ private string type;
+ private string subType;
+ private bool? hasNonQualityFactorParameter;
+ private double? qualityFactor;
+
+ public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaType)
+ {
+ this.mediaType = mediaType;
+ string[] splitMediaType = mediaType.MediaType.Split(MediaTypeSubTypeDelimiter);
+ this.type = splitMediaType[0];
+ this.subType = splitMediaType[1];
+ }
+
+ public string Type
+ {
+ get
+ {
+ return this.type;
+ }
+ }
+
+ public string SubType
+ {
+ get
+ {
+ return this.subType;
+ }
+ }
+
+ public bool IsAllMediaRange
+ {
+ get
+ {
+ return this.IsSubTypeMediaRange && String.Equals(MediaRangeAsterisk, this.Type, StringComparison.Ordinal);
+ }
+ }
+
+ public bool IsSubTypeMediaRange
+ {
+ get
+ {
+ return String.Equals(MediaRangeAsterisk, this.SubType, StringComparison.Ordinal);
+ }
+ }
+
+ public bool HasNonQualityFactorParameter
+ {
+ get
+ {
+ if (!this.hasNonQualityFactorParameter.HasValue)
+ {
+ this.hasNonQualityFactorParameter = false;
+ foreach (NameValueHeaderValue param in this.mediaType.Parameters)
+ {
+ if (!String.Equals(QualityFactorParameterName, param.Name, StringComparison.Ordinal))
+ {
+ this.hasNonQualityFactorParameter = true;
+ }
+ }
+ }
+
+ return this.hasNonQualityFactorParameter.Value;
+ }
+ }
+
+ public string CharSet
+ {
+ get
+ {
+ return this.mediaType.CharSet;
+ }
+ }
+
+ public double QualityFactor
+ {
+ get
+ {
+ if (!this.qualityFactor.HasValue)
+ {
+ MediaTypeWithQualityHeaderValue mediaTypeWithQuality = this.mediaType as MediaTypeWithQualityHeaderValue;
+ if (mediaTypeWithQuality != null)
+ {
+ this.qualityFactor = mediaTypeWithQuality.Quality;
+ }
+
+ if (!this.qualityFactor.HasValue)
+ {
+ this.qualityFactor = DefaultQualityFactor;
+ }
+ }
+
+ return this.qualityFactor.Value;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/RefTypeTestData.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/RefTypeTestData.cs
new file mode 100644
index 0000000000..09866eca4c
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/RefTypeTestData.cs
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.TestCommon
+{
+ public class RefTypeTestData : TestData where T : class
+ {
+ private Func> testDataProvider;
+ private Func> derivedTypeTestDataProvider;
+ private Func> knownTypeTestDataProvider;
+
+ public RefTypeTestData(Func> testDataProvider)
+ {
+ if (testDataProvider == null)
+ {
+ throw new ArgumentNullException("testDataProvider");
+ }
+
+ this.testDataProvider = testDataProvider;
+ this.RegisterTestDataVariation(TestDataVariations.WithNull, this.Type, GetNullTestData);
+ }
+
+ public RefTypeTestData(
+ Func> testDataProvider,
+ Func> derivedTypeTestDataProvider,
+ Func> knownTypeTestDataProvider)
+ : this(testDataProvider)
+ {
+ this.derivedTypeTestDataProvider = derivedTypeTestDataProvider;
+ if (this.derivedTypeTestDataProvider != null)
+ {
+ this.RegisterTestDataVariation(TestDataVariations.AsDerivedType, this.Type, this.GetTestDataAsDerivedType);
+ }
+
+ this.knownTypeTestDataProvider = knownTypeTestDataProvider;
+ if (this.knownTypeTestDataProvider != null)
+ {
+ this.RegisterTestDataVariation(TestDataVariations.AsKnownType, this.Type, this.GetTestDataAsDerivedKnownType);
+ }
+ }
+
+ public T GetNullTestData()
+ {
+ return null;
+ }
+
+ public IEnumerable GetTestDataAsDerivedType()
+ {
+ if (this.derivedTypeTestDataProvider != null)
+ {
+ return this.derivedTypeTestDataProvider();
+ }
+
+ return Enumerable.Empty();
+ }
+
+ public IEnumerable GetTestDataAsDerivedKnownType()
+ {
+ if (this.knownTypeTestDataProvider != null)
+ {
+ return this.knownTypeTestDataProvider();
+ }
+
+ return Enumerable.Empty();
+ }
+
+ protected override IEnumerable GetTypedTestData()
+ {
+ return this.testDataProvider();
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/SimpleEnum.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/SimpleEnum.cs
new file mode 100644
index 0000000000..cb5ca4e452
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/SimpleEnum.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.TestCommon.Types
+{
+ public enum SimpleEnum
+ {
+ First,
+ Second,
+ Third,
+ Fourth
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/TestData.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/TestData.cs
new file mode 100644
index 0000000000..a674f3fc53
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/TestUtils/TestData.cs
@@ -0,0 +1,441 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Microsoft.TestCommon.Types;
+
+namespace Microsoft.TestCommon
+{
+ ///
+ /// A base class for test data. A instance is associated with a given type, and the instance can
+ /// provide instances of the given type to use as data in tests. The same instance can also provide instances
+ /// of types related to the given type, such as a of the type. See the enum for all the
+ /// variations of test data that a instance can provide.
+ ///
+ public abstract class TestData
+ {
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData CharTestData = new ValueTypeTestData('a', Char.MinValue, Char.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData IntTestData = new ValueTypeTestData(-1, 0, 1, Int32.MinValue, Int32.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData UintTestData = new ValueTypeTestData(0, 1, UInt32.MinValue, UInt32.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData ShortTestData = new ValueTypeTestData(-1, 0, 1, Int16.MinValue, Int16.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData UshortTestData = new ValueTypeTestData(0, 1, UInt16.MinValue, UInt16.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData LongTestData = new ValueTypeTestData(-1, 0, 1, Int64.MinValue, Int64.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData UlongTestData = new ValueTypeTestData(0, 1, UInt64.MinValue, UInt64.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData ByteTestData = new ValueTypeTestData(0, 1, Byte.MinValue, Byte.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData SByteTestData = new ValueTypeTestData(-1, 0, 1, SByte.MinValue, SByte.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData BoolTestData = new ValueTypeTestData(true, false);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData DoubleTestData = new ValueTypeTestData(
+ -1.0,
+ 0.0,
+ 1.0,
+ double.MinValue,
+ double.MaxValue,
+ double.PositiveInfinity,
+ double.NegativeInfinity);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData FloatTestData = new ValueTypeTestData(
+ -1.0f,
+ 0.0f,
+ 1.0f,
+ float.MinValue,
+ float.MaxValue,
+ float.PositiveInfinity,
+ float.NegativeInfinity);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData DecimalTestData = new ValueTypeTestData(
+ -1M,
+ 0M,
+ 1M,
+ decimal.MinValue,
+ decimal.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData DateTimeTestData = new ValueTypeTestData(
+ DateTime.Now,
+ DateTime.UtcNow,
+ DateTime.MaxValue,
+ DateTime.MinValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData TimeSpanTestData = new ValueTypeTestData(
+ TimeSpan.MinValue,
+ TimeSpan.MaxValue);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData GuidTestData = new ValueTypeTestData(
+ Guid.NewGuid(),
+ Guid.Empty);
+
+ ///
+ /// Common for a .
+ ///
+ public static readonly ValueTypeTestData DateTimeOffsetTestData = new ValueTypeTestData(
+ DateTimeOffset.MaxValue,
+ DateTimeOffset.MinValue,
+ new DateTimeOffset(DateTime.Now));
+
+ ///
+ /// Common for an enum.
+ ///
+ public static readonly ValueTypeTestData SimpleEnumTestData = new ValueTypeTestData(
+ SimpleEnum.First,
+ SimpleEnum.Second,
+ SimpleEnum.Third);
+
+ ///
+ /// Common for an enum implemented with a .
+ ///
+ public static readonly ValueTypeTestData LongEnumTestData = new ValueTypeTestData(
+ LongEnum.FirstLong,
+ LongEnum.SecondLong,
+ LongEnum.ThirdLong);
+
+ ///
+ /// Common for an enum decorated with a .
+ ///
+ public static readonly ValueTypeTestData FlagsEnumTestData = new ValueTypeTestData(
+ FlagsEnum.One,
+ FlagsEnum.Two,
+ FlagsEnum.Four);
+
+ ///
+ /// Expected permutations of non supported file paths.
+ ///
+ public static readonly TestData NotSupportedFilePaths = new RefTypeTestData(() => new List() {
+ "cc:\\a\\b",
+ });
+
+ ///
+ /// Expected permutations of invalid file paths.
+ ///
+ public static readonly TestData InvalidNonNullFilePaths = new RefTypeTestData(() => new List() {
+ String.Empty,
+ "",
+ " ",
+ " ",
+ "\t\t \n ",
+ "c:\\a