Remove runtime dependency usage of MediaTypeHeaderValue.

Removes usage of MediaTypeHeaderValue in ApiExplorer, InputFormatters and
OutputFormatters

Public interface changes with stub implementation.
This commit is contained in:
jacalvar 2015-12-10 11:15:33 -08:00
parent 32d15186a0
commit 830fd410f5
76 changed files with 1061 additions and 496 deletions

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Net.Http.Headers;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Mvc.Formatters
{
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// and expect to see the same value provided in
/// <see cref="IOutputFormatter.WriteAsync(OutputFormatterWriteContext)"/>
/// </remarks>
public virtual MediaTypeHeaderValue ContentType { get; set; }
public virtual StringSegment ContentType { get; set; }
/// <summary>
/// Gets or sets a value indicating that content-negotiation could not find a formatter based on the

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.ApiExplorer
{
@ -19,6 +18,6 @@ namespace Microsoft.AspNet.Mvc.ApiExplorer
/// <summary>
/// The media type of the response.
/// </summary>
public MediaTypeHeaderValue MediaType { get; set; }
public string MediaType { get; set; }
}
}

View File

@ -15,7 +15,6 @@ using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Routing.Template;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.ApiExplorer
{
@ -311,7 +310,7 @@ namespace Microsoft.AspNet.Mvc.ApiExplorer
// Walk through all 'filter' attributes in order, and allow each one to see or override
// the results of the previous ones. This is similar to the execution path for content-negotiation.
var contentTypes = new List<MediaTypeHeaderValue>();
var contentTypes = new MediaTypeCollection();
if (responseMetadataAttributes != null)
{
foreach (var metadataAttribute in responseMetadataAttributes)
@ -322,7 +321,7 @@ namespace Microsoft.AspNet.Mvc.ApiExplorer
if (contentTypes.Count == 0)
{
contentTypes.Add(null);
contentTypes.Add((string)null);
}
foreach (var contentType in contentTypes)

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.ApiExplorer
{
@ -28,8 +27,8 @@ namespace Microsoft.AspNet.Mvc.ApiExplorer
/// The <see cref="Type"/> for which the supported content types are desired.
/// </param>
/// <returns>Content types which are supported by the <see cref="Formatters.IOutputFormatter"/>.</returns>
IReadOnlyList<MediaTypeHeaderValue> GetSupportedContentTypes(
MediaTypeHeaderValue contentType,
IReadOnlyList<string> GetSupportedContentTypes(
string contentType,
Type objectType);
}
}

View File

@ -2,8 +2,7 @@
// 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 Microsoft.Net.Http.Headers;
using Microsoft.AspNet.Mvc.Formatters;
namespace Microsoft.AspNet.Mvc.ApiExplorer
{
@ -20,6 +19,6 @@ namespace Microsoft.AspNet.Mvc.ApiExplorer
/// <summary>
/// Configures a collection of allowed content types which can be produced by the action.
/// </summary>
void SetContentTypes(IList<MediaTypeHeaderValue> contentTypes);
void SetContentTypes(MediaTypeCollection contentTypes);
}
}

View File

@ -8,6 +8,8 @@ using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ActionConstraints;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
@ -30,6 +32,15 @@ namespace Microsoft.AspNet.Mvc
throw new ArgumentNullException(nameof(contentType));
}
// We want to ensure that the given provided content types are valid values, so
// we validate them using the semantics of MediaTypeHeaderValue.
MediaTypeHeaderValue.Parse(contentType);
for (var i = 0; i < otherContentTypes.Length; i++)
{
MediaTypeHeaderValue.Parse(otherContentTypes[i]);
}
ContentTypes = GetContentTypes(contentType, otherContentTypes);
}
@ -39,7 +50,7 @@ namespace Microsoft.AspNet.Mvc
int IActionConstraint.Order { get; } = ConsumesActionConstraintOrder;
/// <inheritdoc />
public IList<MediaTypeHeaderValue> ContentTypes { get; set; }
public MediaTypeCollection ContentTypes { get; set; }
/// <inheritdoc />
public void OnResourceExecuting(ResourceExecutingContext context)
@ -53,13 +64,12 @@ namespace Microsoft.AspNet.Mvc
// Ignore all other filters. This is to ensure we have a overriding behavior.
if (IsApplicable(context.ActionDescriptor))
{
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(context.HttpContext.Request.ContentType, out requestContentType);
var requestContentType = context.HttpContext.Request.ContentType;
// Confirm the request's content type is more specific than a media type this action supports e.g. OK
// if client sent "text/plain" data and this action supports "text/*".
if (requestContentType != null &&
!ContentTypes.Any(contentType => requestContentType.IsSubsetOf(contentType)))
!ContentTypes.Any(contentType => MediaTypeComparisons.IsSubsetOf(contentType, requestContentType)))
{
context.Result = new UnsupportedMediaTypeResult();
}
@ -86,8 +96,7 @@ namespace Microsoft.AspNet.Mvc
return true;
}
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(context.RouteContext.HttpContext.Request.ContentType, out requestContentType);
var requestContentType = context.RouteContext.HttpContext.Request.ContentType;
// If the request content type is null we need to act like pass through.
// In case there is a single candidate with a constraint it should be selected.
@ -104,7 +113,7 @@ namespace Microsoft.AspNet.Mvc
// Confirm the request's content type is more specific than a media type this action supports e.g. OK
// if client sent "text/plain" data and this action supports "text/*".
if (ContentTypes.Any(contentType => requestContentType.IsSubsetOf(contentType)))
if (ContentTypes.Any(contentType => MediaTypeComparisons.IsSubsetOf(contentType, requestContentType)))
{
return true;
}
@ -164,22 +173,22 @@ namespace Microsoft.AspNet.Mvc
}
private List<MediaTypeHeaderValue> GetContentTypes(string firstArg, string[] args)
private MediaTypeCollection GetContentTypes(string firstArg, string[] args)
{
var completeArgs = new List<string>();
completeArgs.Add(firstArg);
completeArgs.AddRange(args);
var contentTypes = new List<MediaTypeHeaderValue>();
var contentTypes = new MediaTypeCollection();
foreach (var arg in completeArgs)
{
var contentType = MediaTypeHeaderValue.Parse(arg);
if (contentType.MatchesAllSubTypes || contentType.MatchesAllTypes)
if (MediaTypeComparisons.MatchesAllSubtypes(arg) ||
MediaTypeComparisons.MatchesAllTypes(arg))
{
throw new InvalidOperationException(
Resources.FormatMatchAllContentTypeIsNotAllowed(arg));
}
contentTypes.Add(contentType);
contentTypes.Add(arg);
}
return contentTypes;

View File

@ -16,10 +16,10 @@ namespace Microsoft.AspNet.Mvc
{
public class ContentResult : ActionResult
{
private readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("text/plain")
private readonly string DefaultContentType = new MediaTypeHeaderValue("text/plain")
{
Encoding = Encoding.UTF8
};
}.ToString();
/// <summary>
/// Gets or set the content representing the body of the response.
@ -27,9 +27,9 @@ namespace Microsoft.AspNet.Mvc
public string Content { get; set; }
/// <summary>
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// Gets or sets the Content-Type header for the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; set; }
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.

View File

@ -271,7 +271,7 @@ namespace Microsoft.AspNet.Mvc
var result = new ContentResult
{
Content = content,
ContentType = contentType
ContentType = contentType?.ToString()
};
return result;

View File

@ -31,10 +31,6 @@ namespace Microsoft.AspNet.Mvc
{
throw new ArgumentNullException(nameof(fileContents));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
}
/// <summary>
@ -45,18 +41,13 @@ namespace Microsoft.AspNet.Mvc
/// <param name="fileContents">The bytes that represent the file contents.</param>
/// <param name="contentType">The Content-Type header of the response.</param>
public FileContentResult(byte[] fileContents, MediaTypeHeaderValue contentType)
: base(contentType)
: base(contentType?.ToString())
{
if (fileContents == null)
{
throw new ArgumentNullException(nameof(fileContents));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
FileContents = fileContents;
}

View File

@ -7,6 +7,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Mvc
/// the provided <paramref name="contentType"/>.
/// </summary>
/// <param name="contentType">The Content-Type header of the response.</param>
protected FileResult(MediaTypeHeaderValue contentType)
protected FileResult(string contentType)
{
if (contentType == null)
{
@ -35,9 +36,9 @@ namespace Microsoft.AspNet.Mvc
}
/// <summary>
/// Gets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// Gets the Content-Type header for the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; }
public string ContentType { get; }
/// <summary>
/// Gets the file name that will be used in the Content-Disposition header of the response.

View File

@ -42,18 +42,13 @@ namespace Microsoft.AspNet.Mvc
/// <param name="fileStream">The stream with the file.</param>
/// <param name="contentType">The Content-Type header of the response.</param>
public FileStreamResult(Stream fileStream, MediaTypeHeaderValue contentType)
: base(contentType)
: base(contentType?.ToString())
{
if (fileStream == null)
{
throw new ArgumentNullException(nameof(fileStream));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
FileStream = fileStream;
}

View File

@ -2,12 +2,10 @@
// 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;
using Microsoft.AspNet.Mvc.ApiExplorer;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
@ -78,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Determine media types this action supports.
var responseTypeFilters = context.Filters.OfType<IApiResponseMetadataProvider>();
var supportedMediaTypes = new List<MediaTypeHeaderValue>();
var supportedMediaTypes = new MediaTypeCollection();
foreach (var filter in responseTypeFilters)
{
filter.SetContentTypes(supportedMediaTypes);
@ -91,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// request's format and IApiResponseMetadataProvider-provided content types similarly to an Accept
// header and an output formatter's SupportedMediaTypes: Confirm action supports a more specific media
// type than requested e.g. OK if "text/*" requested and action supports "text/plain".
if (!supportedMediaTypes.Any(c => c.IsSubsetOf(contentType)))
if (!supportedMediaTypes.Any(c => MediaTypeComparisons.IsSubsetOf(contentType, c)))
{
context.Result = new HttpNotFoundResult();
}

View File

@ -4,24 +4,46 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
/// <summary>
/// Used to specify mapping between the URL Format and corresponding <see cref="MediaTypeHeaderValue"/>.
/// Used to specify mapping between the URL Format and corresponding media type.
/// </summary>
public class FormatterMappings
{
private readonly Dictionary<string, MediaTypeHeaderValue> _map =
new Dictionary<string, MediaTypeHeaderValue>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, string> _map =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Sets mapping for the format to specified <see cref="MediaTypeHeaderValue"/>.
/// If the format already exists, the <see cref="MediaTypeHeaderValue"/> will be overwritten with the new value.
/// Sets mapping for the format to specified media type.
/// If the format already exists, the media type will be overwritten with the new value.
/// </summary>
/// <param name="format">The format value.</param>
/// <param name="contentType">The <see cref="MediaTypeHeaderValue"/> for the format value.</param>
/// <param name="contentType">The media type for the format value.</param>
public void SetMediaTypeMappingForFormat(string format, string contentType)
{
if (format == null)
{
throw new ArgumentNullException(nameof(format));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
SetMediaTypeMappingForFormat(format, MediaTypeHeaderValue.Parse(contentType));
}
/// <summary>
/// Sets mapping for the format to specified media type.
/// If the format already exists, the media type will be overwritten with the new value.
/// </summary>
/// <param name="format">The format value.</param>
/// <param name="contentType">The media type for the format value.</param>
public void SetMediaTypeMappingForFormat(string format, MediaTypeHeaderValue contentType)
{
if (format == null)
@ -36,31 +58,34 @@ namespace Microsoft.AspNet.Mvc.Formatters
ValidateContentType(contentType);
format = RemovePeriodIfPresent(format);
_map[format] = contentType.CopyAsReadOnly();
_map[format] = contentType.ToString();
}
/// <summary>
/// Gets <see cref="MediaTypeHeaderValue"/> for the specified format.
/// Gets the media type for the specified format.
/// </summary>
/// <param name="format">The format value.</param>
/// <returns>The <see cref="MediaTypeHeaderValue"/> for input format.</returns>
public MediaTypeHeaderValue GetMediaTypeMappingForFormat(string format)
/// <returns>The media type for input format.</returns>
public string GetMediaTypeMappingForFormat(string format)
{
if (format == null)
if (string.IsNullOrEmpty(format))
{
throw new ArgumentNullException(nameof(format));
var message = Resources.FormatFormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat(
nameof(format));
throw new ArgumentException(message, nameof(format));
}
format = RemovePeriodIfPresent(format);
MediaTypeHeaderValue value = null;
string value = null;
_map.TryGetValue(format, out value);
return value;
}
/// <summary>
/// Clears the <see cref="MediaTypeHeaderValue"/> mapping for the format.
/// Clears the media type mapping for the format.
/// </summary>
/// <param name="format">The format value.</param>
/// <returns><c>true</c> if the format is successfully found and cleared; otherwise, <c>false</c>.</returns>

View File

@ -8,6 +8,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
@ -37,10 +38,10 @@ namespace Microsoft.AspNet.Mvc.Formatters
public IList<Encoding> SupportedEncodings { get; } = new List<Encoding>();
/// <summary>
/// Gets the mutable collection of <see cref="MediaTypeHeaderValue"/> elements supported by
/// Gets the mutable collection of media type elements supported by
/// this <see cref="InputFormatter"/>.
/// </summary>
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; } = new List<MediaTypeHeaderValue>();
public MediaTypeCollection SupportedMediaTypes { get; } = new MediaTypeCollection();
protected object GetDefaultValueForType(Type modelType)
{
@ -61,8 +62,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
var contentType = context.HttpContext.Request.ContentType;
MediaTypeHeaderValue requestContentType;
if (!MediaTypeHeaderValue.TryParse(contentType, out requestContentType))
if (string.IsNullOrEmpty(contentType))
{
return false;
}
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// client sent "text/plain" data and this formatter supports "text/*".
return SupportedMediaTypes.Any(supportedMediaType =>
{
return requestContentType.IsSubsetOf(supportedMediaType);
return MediaTypeComparisons.IsSubsetOf(supportedMediaType, contentType);
});
}
@ -107,27 +107,28 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// <summary>
/// Returns an <see cref="Encoding"/> based on <paramref name="context"/>'s
/// <see cref="MediaTypeHeaderValue.Charset"/>.
/// character set.
/// </summary>
/// <param name="context">The <see cref="InputFormatterContext"/>.</param>
/// <returns>
/// An <see cref="Encoding"/> based on <paramref name="context"/>'s
/// <see cref="MediaTypeHeaderValue.Charset"/>. <c>null</c> if no supported encoding was found.
/// character set. <c>null</c> if no supported encoding was found.
/// </returns>
protected Encoding SelectCharacterEncoding(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue contentType;
MediaTypeHeaderValue.TryParse(request.ContentType, out contentType);
if (contentType != null)
if (request.ContentType != null)
{
var charset = contentType.Charset;
if (!string.IsNullOrEmpty(charset))
var encoding = MediaTypeEncoding.GetEncoding(request.ContentType);
if (encoding != null)
{
foreach (var supportedEncoding in SupportedEncodings)
{
if (string.Equals(charset, supportedEncoding.WebName, StringComparison.OrdinalIgnoreCase))
if (string.Equals(
encoding.WebName,
supportedEncoding.WebName,
StringComparison.OrdinalIgnoreCase))
{
return supportedEncoding;
}

View File

@ -0,0 +1,73 @@
// Copyright (c) .NET Foundation. 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.ObjectModel;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
/// <summary>
/// A collection of media types.
/// </summary>
public class MediaTypeCollection : Collection<string>
{
/// <summary>
/// Initializes a new instance of <see cref="MediaTypeCollection"/>.
/// </summary>
public MediaTypeCollection()
{
}
/// <summary>
/// Adds an object to the end of the <see cref="MediaTypeCollection"/>.
/// </summary>
/// <param name="item">The media type to be added to the end of the <see cref="MediaTypeCollection"/>.</param>
public void Add(MediaTypeHeaderValue item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
Add(item.ToString());
}
/// <summary>
/// Inserts an element into the <see cref="MediaTypeCollection"/> at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
/// <param name="item">The media type to insert.</param>
public void Insert(int index, MediaTypeHeaderValue item)
{
if (index < 0 || index > Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
Insert(index, item.ToString());
}
/// <summary>
/// Removes the first occurrence of a specific media type from the <see cref="MediaTypeCollection"/>.
/// </summary>
/// <param name="item"></param>
/// <returns><code>true</code> if <paramref name="item"/> is successfully removed; otherwise, <code>false</code>.
/// This method also returns <code>false</code> if <paramref name="item"/> was not found in the original
/// <see cref="MediaTypeCollection"/>.</returns>
public bool Remove(MediaTypeHeaderValue item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
return Remove(item.ToString());
}
}
}

View File

@ -0,0 +1,120 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
/// <summary>
/// Different types of tests against media type values.
/// </summary>
public static class MediaTypeComparisons
{
/// <summary>
/// Determines if the <paramref name="subset" /> media type is a subset of the <paramref name="set" /> media type
/// without taking into account the quality parameter.
/// </summary>
/// <param name="set">The more general media type.</param>
/// <param name="subset">The more specific media type.</param>
/// <returns><code>true</code> if <paramref name="set" /> is a more general media type than <paramref name="subset"/>;
/// otherwise <code>false</code>.</returns>
public static bool IsSubsetOf(StringSegment set, string subset)
{
return IsSubsetOf(set, new StringSegment(subset));
}
/// <summary>
/// Determines if the <paramref name="subset" /> media type is a subset of the <paramref name="set" /> media type
/// without taking into account the quality parameter.
/// </summary>
/// <param name="set">The more general media type.</param>
/// <param name="subset">The more specific media type.</param>
/// <returns><code>true</code> if <paramref name="set" /> is a more general media type than <paramref name="subset"/>;
/// otherwise <code>false</code>.</returns>
public static bool IsSubsetOf(string set, string subset)
{
return IsSubsetOf(new StringSegment(set), new StringSegment(subset));
}
/// <summary>
/// Determines if the <paramref name="subset" /> media type is a subset of the <paramref name="set" /> media type.
/// Two media types are compatible if one is a subset of the other ignoring any charset
/// parameter.
/// </summary>
/// <param name="set">The more general media type.</param>
/// <param name="subset">The more specific media type.</param>
/// <param name="ignoreQuality">Whether or not we should skip checking the quality parameter.</param>
/// <returns><code>true</code> if <paramref name="set" /> is a more general media type than <paramref name="subset"/>;
/// otherwise <code>false</code>.</returns>
public static bool IsSubsetOf(StringSegment set, StringSegment subset)
{
if (!set.HasValue || !subset.HasValue)
{
return false;
}
MediaTypeHeaderValue setMediaType;
MediaTypeHeaderValue subSetMediaType;
return MediaTypeHeaderValue.TryParse(set.Value, out setMediaType) &&
MediaTypeHeaderValue.TryParse(subset.Value, out subSetMediaType) &&
subSetMediaType.IsSubsetOf(setMediaType);
}
/// <summary>
/// Determines if the type of a given <paramref name="mediaType" /> matches all types, E.g, */*.
/// </summary>
/// <param name="mediaType">The media type to check</param>
/// <returns><code>true</code> if the <paramref name="mediaType" /> matches all subtypes; otherwise <code>false</code>.</returns>
public static bool MatchesAllTypes(string mediaType)
{
return MatchesAllTypes(new StringSegment(mediaType));
}
/// <summary>
/// Determines if the type of a given <paramref name="mediaType" /> matches all types, E.g, */*.
/// </summary>
/// <param name="mediaType">The media type to check</param>
/// <returns><code>true</code> if the <paramref name="mediaType" /> matches all subtypes; otherwise <code>false</code>.</returns>
public static bool MatchesAllTypes(StringSegment mediaType)
{
if (!mediaType.HasValue)
{
return false;
}
MediaTypeHeaderValue parsedMediaType;
return MediaTypeHeaderValue.TryParse(mediaType.Value, out parsedMediaType) &&
parsedMediaType.MatchesAllTypes;
}
/// <summary>
/// Determines if the given <paramref name="mediaType" /> matches all subtypes, E.g, text/*.
/// </summary>
/// <param name="mediaType">The media type to check</param>
/// <returns><code>true</code> if the <paramref name="mediaType" /> matches all subtypes; otherwise <code>false</code>.</returns>
public static bool MatchesAllSubtypes(string mediaType)
{
return MatchesAllSubtypes(new StringSegment(mediaType));
}
/// <summary>
/// Determines if the given <paramref name="mediaType" /> matches all subtypes, E.g, text/*.
/// </summary>
/// <param name="mediaType">The media type to check</param>
/// <returns><code>true</code> if the <paramref name="mediaType" /> matches all subtypes; otherwise <code>false</code>.</returns>
public static bool MatchesAllSubtypes(StringSegment mediaType)
{
if (!mediaType.HasValue)
{
return false;
}
MediaTypeHeaderValue parsedMediaType;
return MediaTypeHeaderValue.TryParse(mediaType.Value, out parsedMediaType) &&
parsedMediaType.MatchesAllSubTypes;
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Text;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
/// <summary>
/// A set of operations to manipulate the encoding of a media type value.
/// </summary>
public class MediaTypeEncoding
{
/// <summary>
/// Gets the <see cref="Encoding"/> for the given <see cref="mediaType"/> if it exists.
/// </summary>
/// <param name="mediaType">The media type from which to get the charset parameter.</param>
/// <returns>The <see cref="Encoding"/> of the media type if it exists; otherwise <code>null</code>.</returns>
public static Encoding GetEncoding(StringSegment mediaType)
{
var charset = GetCharsetParameter(mediaType);
return GetEncodingFromCharset(charset);
}
/// <summary>
/// Gets the <see cref="Encoding"/> for the given <see cref="mediaType"/> if it exists.
/// </summary>
/// <param name="mediaType">The media type from which to get the charset parameter.</param>
/// <returns>The <see cref="Encoding"/> of the media type if it exists or a <see cref="StringSegment"/> without value if not.</returns>
public static Encoding GetEncoding(string mediaType)
{
var charset = GetCharsetParameter(new StringSegment(mediaType));
return GetEncodingFromCharset(charset);
}
/// <summary>
/// Gets the charset parameter of the given <paramref name="mediaType"/> if it exists.
/// </summary>
/// <param name="mediaType">The media type from which to get the charset parameter.</param>
/// <returns>The charset of the media type if it exists or a <see cref="StringSegment"/> without value if not.</returns>
public static StringSegment GetCharsetParameter(StringSegment mediaType)
{
MediaTypeHeaderValue parsedMediaType;
if (MediaTypeHeaderValue.TryParse(mediaType.Value, out parsedMediaType))
{
return new StringSegment(parsedMediaType.Charset);
}
return new StringSegment();
}
/// <summary>
/// Replaces the encoding of the given <paramref name="mediaType"/> with the provided
/// <paramref name="encoding"/>.
/// </summary>
/// <param name="mediaType">The media type whose encoding will be replaced.</param>
/// <param name="encoding">The encoding that will replace the encoding in the <paramref name="mediaType"/></param>
/// <returns>A media type with the replaced encoding.</returns>
public static string ReplaceEncoding(string mediaType, Encoding encoding)
{
return ReplaceEncoding(new StringSegment(mediaType), encoding);
}
/// <summary>
/// Replaces the encoding of the given <paramref name="mediaType"/> with the provided
/// <paramref name="encoding"/>.
/// </summary>
/// <param name="mediaType">The media type whose encoding will be replaced.</param>
/// <param name="encoding">The encoding that will replace the encoding in the <paramref name="mediaType"/></param>
/// <returns>A media type with the replaced encoding.</returns>
public static string ReplaceEncoding(StringSegment mediaType, Encoding encoding)
{
var parsedMediaType = MediaTypeHeaderValue.Parse(mediaType.Value);
parsedMediaType.Encoding = encoding;
return parsedMediaType.ToString();
}
private static Encoding GetEncodingFromCharset(StringSegment charset)
{
if (charset.Equals("utf-8", StringComparison.OrdinalIgnoreCase))
{
// This is an optimization for utf-8 that prevents the Substring caused by
// charset.Value
return Encoding.UTF8;
}
try
{
// charset.Value might be an invalid encoding name as in charset=invalid.
// For that reason, we catch the exception thrown by Encoding.GetEncoding
// and return null instead.
return charset.HasValue ? Encoding.GetEncoding(charset.Value) : null;
}
catch (Exception)
{
return null;
}
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Mvc.Formatters
{
/// <summary>
/// A media type with its associated quality.
/// </summary>
public struct MediaTypeSegmentWithQuality
{
/// <summary>
/// Initializes an instance of <see cref="MediaTypeSegmentWithQuality"/>.
/// </summary>
/// <param name="mediaType">The <see cref="StringSegment"/> containing the media type.</param>
/// <param name="quality">The quality parameter of the media type or 1 in the case it does not exist.</param>
public MediaTypeSegmentWithQuality(StringSegment mediaType, double quality)
{
MediaType = mediaType;
Quality = quality;
}
/// <summary>
/// Gets the media type of this <see cref="MediaTypeSegmentWithQuality"/>.
/// </summary>
public StringSegment MediaType { get; }
/// <summary>
/// Gets the quality of this <see cref="MediaTypeSegmentWithQuality"/>.
/// </summary>
public double Quality { get; }
/// <inheritdoc />
public override string ToString()
{
// For logging purposes
return MediaType.ToString();
}
}
}

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ApiExplorer;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
@ -17,16 +18,13 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// </summary>
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
{
// using a field so we can return it as both IList and IReadOnlyList
private readonly List<MediaTypeHeaderValue> _supportedMediaTypes;
/// <summary>
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
/// </summary>
protected OutputFormatter()
{
SupportedEncodings = new List<Encoding>();
_supportedMediaTypes = new List<MediaTypeHeaderValue>();
SupportedMediaTypes = new MediaTypeCollection();
}
/// <summary>
@ -37,13 +35,10 @@ namespace Microsoft.AspNet.Mvc.Formatters
public IList<Encoding> SupportedEncodings { get; }
/// <summary>
/// Gets the mutable collection of <see cref="MediaTypeHeaderValue"/> elements supported by
/// Gets the mutable collection of media type elements supported by
/// this <see cref="OutputFormatter"/>.
/// </summary>
public IList<MediaTypeHeaderValue> SupportedMediaTypes
{
get { return _supportedMediaTypes; }
}
public MediaTypeCollection SupportedMediaTypes { get; }
/// <summary>
/// Returns a value indicating whether or not the given type can be written by this serializer.
@ -56,8 +51,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
/// <inheritdoc />
public virtual IReadOnlyList<MediaTypeHeaderValue> GetSupportedContentTypes(
MediaTypeHeaderValue contentType,
public virtual IReadOnlyList<string> GetSupportedContentTypes(
string contentType,
Type objectType)
{
if (!CanWriteType(objectType))
@ -68,21 +63,21 @@ namespace Microsoft.AspNet.Mvc.Formatters
if (contentType == null)
{
// If contentType is null, then any type we support is valid.
return _supportedMediaTypes.Count > 0 ? _supportedMediaTypes : null;
return SupportedMediaTypes.Count > 0 ? SupportedMediaTypes : null;
}
else
{
List<MediaTypeHeaderValue> mediaTypes = null;
List<string> mediaTypes = null;
// Confirm this formatter supports a more specific media type than requested e.g. OK if "text/*"
// requested and formatter supports "text/plain". Treat contentType like it came from an Accept header.
foreach (var mediaType in _supportedMediaTypes)
foreach (var mediaType in SupportedMediaTypes)
{
if (mediaType.IsSubsetOf(contentType))
if (MediaTypeComparisons.IsSubsetOf(new StringSegment(contentType), mediaType))
{
if (mediaTypes == null)
{
mediaTypes = new List<MediaTypeHeaderValue>();
mediaTypes = new List<string>();
}
mediaTypes.Add(mediaType);
@ -114,15 +109,19 @@ namespace Microsoft.AspNet.Mvc.Formatters
return encoding;
}
var charset = context.ContentType?.Charset;
if (charset != null)
if (context.ContentType.HasValue)
{
for (var i = 0; i < SupportedEncodings.Count; i++)
var contentTypeEncoding = MediaTypeEncoding.GetCharsetParameter(context.ContentType);
if (contentTypeEncoding.HasValue)
{
if (string.Equals(charset, SupportedEncodings[i].WebName, StringComparison.OrdinalIgnoreCase))
for (var i = 0; i < SupportedEncodings.Count; i++)
{
// This is supported.
return context.ContentType.Encoding;
var supportedEncoding = SupportedEncodings[i];
if (contentTypeEncoding.Equals(supportedEncoding.WebName, StringComparison.OrdinalIgnoreCase))
{
// This is supported.
return SupportedEncodings[i];
}
}
}
}
@ -138,19 +137,19 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
throw new ArgumentNullException(nameof(context));
}
if (!CanWriteType(context.ObjectType))
{
return false;
}
if (context.ContentType == null)
if (!context.ContentType.HasValue)
{
// If the desired content type is set to null, then the current formatter can write anything
// it wants.
if (SupportedMediaTypes.Count > 0)
{
context.ContentType = SupportedMediaTypes[0];
context.ContentType = new StringSegment(SupportedMediaTypes[0]);
return true;
}
else
@ -163,12 +162,13 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Confirm this formatter supports a more specific media type than requested e.g. OK if "text/*"
// requested and formatter supports "text/plain". contentType is typically what we got in an Accept
// header.
var contentType = context.ContentType;
for (var i = 0; i < SupportedMediaTypes.Count; i++)
{
var mediaType = SupportedMediaTypes[i];
if (mediaType.IsSubsetOf(context.ContentType))
var supportedMediaType = SupportedMediaTypes[i];
if (MediaTypeComparisons.IsSubsetOf(contentType, supportedMediaType))
{
context.ContentType = mediaType;
context.ContentType = new StringSegment(SupportedMediaTypes[i]);
return true;
}
}
@ -186,12 +186,12 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
var selectedMediaType = context.ContentType;
if (selectedMediaType == null)
if (!selectedMediaType.HasValue)
{
// If content type is not set then set it based on supported media types.
if (SupportedEncodings.Count > 0)
{
selectedMediaType = SupportedMediaTypes[0];
selectedMediaType = new StringSegment(SupportedMediaTypes[0]);
}
else
{
@ -199,9 +199,6 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
}
// Copy the media type as it may be a 'frozen' instance.
selectedMediaType = selectedMediaType.Copy();
// Note: Text-based media types will use an encoding/charset - binary formats just ignore it. We want to
// make this class work with media types that use encodings, and those that don't.
//
@ -215,7 +212,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
if (selectedEncoding != null)
{
// Override the content type value even if one already existed.
selectedMediaType.Encoding = selectedEncoding;
var mediaTypeWithCharset = GetMediaTypeWithCharset(selectedMediaType.Value, selectedEncoding);
selectedMediaType = new StringSegment(mediaTypeWithCharset);
}
context.ContentType = selectedMediaType;
@ -236,7 +234,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
var response = context.HttpContext.Response;
response.ContentType = context.ContentType?.ToString();
response.ContentType = context.ContentType.Value;
}
/// <summary>
@ -246,6 +244,33 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// <returns>A task which can write the response body.</returns>
public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context);
/// <summary>
/// Adds or replaces the charset parameter in a given <paramref name="mediaType"/> with the
/// given <paramref name="encoding"/>.
/// </summary>
/// <param name="mediaType">The <see cref="StringSegment"/> with the media type.</param>
/// <param name="encoding">
/// The <see cref="Encoding"/> to add or replace in the <paramref name="mediaType"/>.
/// </param>
/// <returns>The mediaType with the given encoding.</returns>
protected string GetMediaTypeWithCharset(string mediaType, Encoding encoding)
{
var mediaTypeEncoding = MediaTypeEncoding.GetEncoding(mediaType);
if (mediaTypeEncoding == encoding)
{
return mediaType;
}
else if (mediaTypeEncoding == null)
{
return CreateMediaTypeWithEncoding(mediaType, encoding);
}
else
{
// This can happen if the user has overriden SelectCharacterEncoding
return MediaTypeEncoding.ReplaceEncoding(mediaType, encoding);
}
}
private Encoding MatchAcceptCharacterEncoding(IList<StringWithQualityHeaderValue> acceptCharsetHeaders)
{
if (acceptCharsetHeaders != null && acceptCharsetHeaders.Count > 0)
@ -329,5 +354,15 @@ namespace Microsoft.AspNet.Mvc.Formatters
sorted.Reverse();
return sorted;
}
private static string CreateMediaTypeWithEncoding(string mediaType, Encoding encoding)
{
return CreateMediaTypeWithEncoding(new StringSegment(mediaType), encoding);
}
private static string CreateMediaTypeWithEncoding(StringSegment mediaType, Encoding encoding)
{
return $"{mediaType.Value}; charset={encoding.WebName}";
}
}
}

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
@ -19,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/plain").CopyAsReadOnly());
SupportedMediaTypes.Add("text/plain");
}
public override bool CanWriteResult(OutputFormatterCanWriteContext context)
@ -33,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// always return it as a text/plain format.
if (context.ObjectType == typeof(string) || context.Object is string)
{
context.ContentType = SupportedMediaTypes[0];
context.ContentType = new StringSegment(SupportedMediaTypes[0]);
return true;
}
@ -55,7 +56,9 @@ namespace Microsoft.AspNet.Mvc.Formatters
var response = context.HttpContext.Response;
return response.WriteAsync(valueAsString, context.ContentType?.Encoding ?? Encoding.UTF8);
return response.WriteAsync(
valueAsString,
MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8);
}
}
}

View File

@ -13,6 +13,7 @@ using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Infrastructure
@ -98,10 +99,10 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
{
if (result.ContentTypes == null)
{
result.ContentTypes = new List<MediaTypeHeaderValue>();
result.ContentTypes = new MediaTypeCollection();
}
result.ContentTypes.Add(MediaTypeHeaderValue.Parse(responseContentType));
result.ContentTypes.Add(responseContentType);
}
}
@ -157,7 +158,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
/// </returns>
protected virtual IOutputFormatter SelectFormatter(
OutputFormatterWriteContext formatterContext,
IList<MediaTypeHeaderValue> contentTypes,
MediaTypeCollection contentTypes,
IList<IOutputFormatter> formatters)
{
if (formatterContext == null)
@ -185,14 +186,14 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
}
var request = formatterContext.HttpContext.Request;
var acceptValues = PrepareAcceptValues(request.GetTypedHeaders().Accept);
var mediaTypes = GetMediaTypes(contentTypes, request);
IOutputFormatter selectedFormatter = null;
if (contentTypes == null || contentTypes.Count == 0)
if (contentTypes.Count == 0)
{
// Check if we have enough information to do content-negotiation, otherwise get the first formatter
// which can write the type. Let the formatter choose the Content-Type.
if (acceptValues == null || acceptValues.Count == 0)
if (!(mediaTypes.Count > 0))
{
Logger.NoAcceptForNegotiation();
@ -207,13 +208,13 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
selectedFormatter = SelectFormatterUsingSortedAcceptHeaders(
formatterContext,
formatters,
acceptValues);
mediaTypes);
// 2. No formatter was found based on Accept header. Fallback to the first formatter which can write
// the type. Let the formatter choose the Content-Type.
if (selectedFormatter == null)
{
Logger.NoFormatterFromNegotiation(acceptValues);
Logger.NoFormatterFromNegotiation(mediaTypes);
// Set this flag to indicate that content-negotiation has failed to let formatters decide
// if they want to write the response or not.
@ -224,32 +225,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
}
else
{
if (acceptValues != null && acceptValues.Count > 0)
if (mediaTypes.Count > 0)
{
// Filter and remove accept headers which cannot support any of the user specified content types.
// That is, confirm this result supports a more specific media type than requested e.g. OK if
// "text/*" requested and result supports "text/plain".
for (var i = acceptValues.Count - 1; i >= 0; i--)
{
var isCompatible = false;
for (var j = 0; j < contentTypes.Count; j++)
{
if (contentTypes[j].IsSubsetOf(acceptValues[i]))
{
isCompatible = true;
}
}
if (!isCompatible)
{
acceptValues.RemoveAt(i);
}
}
selectedFormatter = SelectFormatterUsingSortedAcceptHeaders(
formatterContext,
formatters,
acceptValues);
mediaTypes);
}
if (selectedFormatter == null)
@ -270,6 +251,58 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
return selectedFormatter;
}
private List<MediaTypeSegmentWithQuality> GetMediaTypes(
MediaTypeCollection contentTypes,
HttpRequest request)
{
var result = new List<MediaTypeSegmentWithQuality>();
var parsedHeaders = request.GetTypedHeaders().Accept;
for (var i = 0; i < parsedHeaders?.Count; i++)
{
result.Add(new MediaTypeSegmentWithQuality(
new StringSegment(parsedHeaders[i].ToString()),
parsedHeaders[i].Quality ?? 1.0));
}
for (var i = 0; i < result.Count; i++)
{
if (!RespectBrowserAcceptHeader &&
MediaTypeComparisons.MatchesAllTypes(result[i].MediaType) &&
MediaTypeComparisons.MatchesAllSubtypes(result[i].MediaType))
{
result.Clear();
return result;
}
if (!InAcceptableMediaTypes(result[i].MediaType, contentTypes))
{
result.RemoveAt(i);
}
}
result.Sort((left, right) => left.Quality > right.Quality ? -1 : (left.Quality == right.Quality ? 0 : 1));
return result;
}
private static bool InAcceptableMediaTypes(StringSegment mediaType, MediaTypeCollection acceptableMediaTypes)
{
if (acceptableMediaTypes.Count == 0)
{
return true;
}
for (int i = 0; i < acceptableMediaTypes.Count; i++)
{
if (MediaTypeComparisons.IsSubsetOf(mediaType, acceptableMediaTypes[i]))
{
return true;
}
}
return false;
}
/// <summary>
/// Selects the <see cref="IOutputFormatter"/> to write the response. The first formatter which
/// can write the response should be chosen without any consideration for content type.
@ -297,7 +330,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
foreach (var formatter in formatters)
{
formatterContext.ContentType = null;
formatterContext.ContentType = new StringSegment();
if (formatter.CanWriteResult(formatterContext))
{
return formatter;
@ -324,7 +357,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
protected virtual IOutputFormatter SelectFormatterUsingSortedAcceptHeaders(
OutputFormatterWriteContext formatterContext,
IList<IOutputFormatter> formatters,
IList<MediaTypeHeaderValue> sortedAcceptHeaders)
IList<MediaTypeSegmentWithQuality> sortedAcceptHeaders)
{
if (formatterContext == null)
{
@ -341,11 +374,13 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
throw new ArgumentNullException(nameof(sortedAcceptHeaders));
}
foreach (var contentType in sortedAcceptHeaders)
for (var i = 0; i < sortedAcceptHeaders.Count; i++)
{
foreach (var formatter in formatters)
var mediaType = sortedAcceptHeaders[i];
formatterContext.ContentType = mediaType.MediaType;
for(var j = 0;j < formatters.Count;j++)
{
formatterContext.ContentType = contentType;
var formatter = formatters[j];
if (formatter.CanWriteResult(formatterContext))
{
return formatter;
@ -373,7 +408,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
protected virtual IOutputFormatter SelectFormatterUsingAnyAcceptableContentType(
OutputFormatterWriteContext formatterContext,
IList<IOutputFormatter> formatters,
IList<MediaTypeHeaderValue> acceptableContentTypes)
MediaTypeCollection acceptableContentTypes)
{
if (formatterContext == null)
{
@ -394,7 +429,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
{
foreach (var contentType in acceptableContentTypes)
{
formatterContext.ContentType = contentType;
formatterContext.ContentType = new StringSegment(contentType);
if (formatter.CanWriteResult(formatterContext))
{
return formatter;
@ -405,90 +440,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
return null;
}
// There's no allocation-free way to sort an IList so we're going to have to live with the
// copy + insertion sort.
private IList<MediaTypeHeaderValue> PrepareAcceptValues(IList<MediaTypeHeaderValue> values)
{
if (values == null || values.Count == 0)
{
return null;
}
// By default we want to ignore considering accept headers for content negotiation when
// they have a media type like */* in them. Browsers typically have these media types.
// In these cases we would want the first formatter in the list of output formatters to
// write the response. This default behavior can be changed through options, so checking here.
if (!RespectBrowserAcceptHeader)
{
for (var i = 0; i < values.Count; i++)
{
if (values[i].MatchesAllTypes)
{
return null;
}
}
}
// Degenerate case, we can avoid copying anything.
if (values.Count == 1)
{
return values;
}
var sortNeeded = false;
var count = 0;
for (var i = 0; i < values.Count; i++)
{
var value = values[i];
if (value.Quality == HeaderQuality.NoMatch)
{
// Exclude this one
}
else if (value.Quality != null)
{
count++;
sortNeeded = true;
}
else
{
count++;
}
}
if (!sortNeeded)
{
return values;
}
var sorted = new List<MediaTypeHeaderValue>(count);
for (var i = 0; i < values.Count; i++)
{
var value = values[i];
if (value.Quality == HeaderQuality.NoMatch)
{
// Exclude this one
}
else
{
var position = sorted.BinarySearch(value, MediaTypeHeaderValueComparer.QualityComparer);
if (position >= 0)
{
sorted.Insert(position + 1, value);
}
else
{
sorted.Insert(~position, value);
}
}
}
// We want a descending sort, but BinarySearch does ascending
sorted.Reverse();
return sorted;
}
private void ValidateContentTypes(IList<MediaTypeHeaderValue> contentTypes)
private void ValidateContentTypes(MediaTypeCollection contentTypes)
{
if (contentTypes == null)
{
@ -498,7 +450,8 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
for (var i = 0; i < contentTypes.Count; i++)
{
var contentType = contentTypes[i];
if (contentType.MatchesAllTypes || contentType.MatchesAllSubTypes)
if (MediaTypeComparisons.MatchesAllTypes(contentType) ||
MediaTypeComparisons.MatchesAllSubtypes(contentType))
{
var message = Resources.FormatObjectResult_MatchAllContentType(
contentType,

View File

@ -3,8 +3,7 @@
using System.Diagnostics;
using System.Text;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Formatters;
namespace Microsoft.AspNet.Mvc.Internal
{
@ -30,44 +29,47 @@ namespace Microsoft.AspNet.Mvc.Internal
/// <param name="resolvedContentType">The content type to be used for the response content type header</param>
/// <param name="resolvedContentTypeEncoding">Encoding to be used for writing the response</param>
public static void ResolveContentTypeAndEncoding(
MediaTypeHeaderValue actionResultContentType,
string actionResultContentType,
string httpResponseContentType,
MediaTypeHeaderValue defaultContentType,
string defaultContentType,
out string resolvedContentType,
out Encoding resolvedContentTypeEncoding)
{
Debug.Assert(defaultContentType != null);
Debug.Assert(defaultContentType.Encoding != null);
var defaultContentTypeEncoding = MediaTypeEncoding.GetEncoding(defaultContentType);
Debug.Assert(defaultContentTypeEncoding != null);
// 1. User sets the ContentType property on the action result
if (actionResultContentType != null)
{
resolvedContentType = actionResultContentType.ToString();
resolvedContentTypeEncoding = actionResultContentType.Encoding ?? defaultContentType.Encoding;
resolvedContentType = actionResultContentType;
var actionResultEncoding = MediaTypeEncoding.GetEncoding(actionResultContentType);
resolvedContentTypeEncoding = actionResultEncoding ?? defaultContentTypeEncoding;
return;
}
// 2. User sets the ContentType property on the http response directly
if (!string.IsNullOrEmpty(httpResponseContentType))
{
MediaTypeHeaderValue mediaType;
if (MediaTypeHeaderValue.TryParse(httpResponseContentType, out mediaType))
var mediaTypeEncoding = MediaTypeEncoding.GetEncoding(httpResponseContentType);
if (mediaTypeEncoding != null)
{
resolvedContentType = httpResponseContentType;
resolvedContentTypeEncoding = mediaType.Encoding ?? defaultContentType.Encoding;
resolvedContentTypeEncoding = mediaTypeEncoding;
}
else
{
resolvedContentType = httpResponseContentType;
resolvedContentTypeEncoding = defaultContentType.Encoding;
resolvedContentTypeEncoding = defaultContentTypeEncoding;
}
return;
}
// 3. Fall-back to the default content type
resolvedContentType = defaultContentType.ToString();
resolvedContentTypeEncoding = defaultContentType.Encoding;
resolvedContentType = defaultContentType;
resolvedContentTypeEncoding = defaultContentTypeEncoding;
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Logging
@ -16,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.Logging
private static readonly Action<ILogger, IOutputFormatter, string, Exception> _formatterSelected;
private static readonly Action<ILogger, string, Exception> _skippedContentNegotiation;
private static readonly Action<ILogger, string, Exception> _noAcceptForNegotiation;
private static readonly Action<ILogger, IEnumerable<MediaTypeHeaderValue>, Exception> _noFormatterFromNegotiation;
private static readonly Action<ILogger, IEnumerable<MediaTypeSegmentWithQuality>, Exception> _noFormatterFromNegotiation;
static ObjectResultExecutorLoggerExtensions()
{
@ -40,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.Logging
LogLevel.Debug,
4,
"No information found on request to perform content negotiation.");
_noFormatterFromNegotiation = LoggerMessage.Define<IEnumerable<MediaTypeHeaderValue>>(
_noFormatterFromNegotiation = LoggerMessage.Define<IEnumerable<MediaTypeSegmentWithQuality>>(
LogLevel.Debug,
5,
"Could not find an output formatter based on content negotiation. Accepted types were ({AcceptTypes})");
@ -67,9 +68,9 @@ namespace Microsoft.AspNet.Mvc.Logging
_formatterSelected(logger, outputFormatter, contentType, null);
}
public static void SkippedContentNegotiation(this ILogger logger, MediaTypeHeaderValue contentType)
public static void SkippedContentNegotiation(this ILogger logger, string contentType)
{
_skippedContentNegotiation(logger, Convert.ToString(contentType), null);
_skippedContentNegotiation(logger, contentType, null);
}
public static void NoAcceptForNegotiation(this ILogger logger)
@ -77,7 +78,7 @@ namespace Microsoft.AspNet.Mvc.Logging
_noAcceptForNegotiation(logger, null, null);
}
public static void NoFormatterFromNegotiation(this ILogger logger, IEnumerable<MediaTypeHeaderValue> acceptTypes)
public static void NoFormatterFromNegotiation(this ILogger logger, IList<MediaTypeSegmentWithQuality> acceptTypes)
{
_noFormatterFromNegotiation(logger, acceptTypes, null);
}

View File

@ -53,8 +53,7 @@ namespace Microsoft.AspNet.Mvc
public FilterCollection Filters { get; }
/// <summary>
/// Used to specify mapping between the URL Format and corresponding
/// <see cref="Net.Http.Headers.MediaTypeHeaderValue"/>.
/// Used to specify mapping between the URL Format and corresponding media type.
/// </summary>
public FormatterMappings FormatterMappings { get; }

View File

@ -2,12 +2,10 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@ -17,14 +15,14 @@ namespace Microsoft.AspNet.Mvc
{
Value = value;
Formatters = new FormatterCollection<IOutputFormatter>();
ContentTypes = new List<MediaTypeHeaderValue>();
ContentTypes = new MediaTypeCollection();
}
public object Value { get; set; }
public FormatterCollection<IOutputFormatter> Formatters { get; set; }
public IList<MediaTypeHeaderValue> ContentTypes { get; set; }
public MediaTypeCollection ContentTypes { get; set; }
public Type DeclaredType { get; set; }

View File

@ -34,10 +34,6 @@ namespace Microsoft.AspNet.Mvc
{
throw new ArgumentNullException(nameof(fileName));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
}
/// <summary>
@ -47,18 +43,13 @@ namespace Microsoft.AspNet.Mvc
/// <param name="fileName">The path to the file. The path must be an absolute path.</param>
/// <param name="contentType">The Content-Type header of the response.</param>
public PhysicalFileResult(string fileName, MediaTypeHeaderValue contentType)
: base(contentType)
: base(contentType?.ToString())
{
if (fileName == null)
{
throw new ArgumentNullException(nameof(fileName));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
FileName = fileName;
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNet.Mvc.ApiExplorer;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
@ -31,7 +32,7 @@ namespace Microsoft.AspNet.Mvc
}
Type = type;
ContentTypes = new List<MediaTypeHeaderValue>();
ContentTypes = new MediaTypeCollection();
}
/// <summary>
@ -46,12 +47,21 @@ namespace Microsoft.AspNet.Mvc
throw new ArgumentNullException(nameof(contentType));
}
// We want to ensure that the given provided content types are valid values, so
// we validate them using the semantics of MediaTypeHeaderValue.
MediaTypeHeaderValue.Parse(contentType);
for (var i = 0; i < additionalContentTypes.Length; i++)
{
MediaTypeHeaderValue.Parse(additionalContentTypes[i]);
}
ContentTypes = GetContentTypes(contentType, additionalContentTypes);
}
public Type Type { get; set; }
public IList<MediaTypeHeaderValue> ContentTypes { get; set; }
public MediaTypeCollection ContentTypes { get; set; }
public override void OnResultExecuting(ResultExecutingContext context)
{
@ -74,16 +84,17 @@ namespace Microsoft.AspNet.Mvc
}
}
private List<MediaTypeHeaderValue> GetContentTypes(string firstArg, string[] args)
private MediaTypeCollection GetContentTypes(string firstArg, string[] args)
{
var completeArgs = new List<string>();
completeArgs.Add(firstArg);
completeArgs.AddRange(args);
var contentTypes = new List<MediaTypeHeaderValue>();
var contentTypes = new MediaTypeCollection();
foreach (var arg in completeArgs)
{
var contentType = MediaTypeHeaderValue.Parse(arg);
if (contentType.MatchesAllSubTypes || contentType.MatchesAllTypes)
var contentType = arg;
if (MediaTypeComparisons.MatchesAllSubtypes(contentType)||
MediaTypeComparisons.MatchesAllTypes(contentType))
{
throw new InvalidOperationException(
Resources.FormatMatchAllContentTypeIsNotAllowed(arg));
@ -95,7 +106,7 @@ namespace Microsoft.AspNet.Mvc
return contentTypes;
}
public void SetContentTypes(IList<MediaTypeHeaderValue> contentTypes)
public void SetContentTypes(MediaTypeCollection contentTypes)
{
contentTypes.Clear();
foreach (var contentType in ContentTypes)

View File

@ -1034,6 +1034,22 @@ namespace Microsoft.AspNet.Mvc.Core
return GetString("HttpResponseStreamWriter_StreamNotWritable");
}
/// <summary>
/// The argument '{0}' is invalid. Empty or null formats are not supported.
/// </summary>
internal static string FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat
{
get { return GetString("FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat"); }
}
/// <summary>
/// The argument '{0}' is invalid. Empty or null formats are not supported.
/// </summary>
internal static string FormatFormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -319,4 +319,7 @@
<data name="HttpResponseStreamWriter_StreamNotWritable" xml:space="preserve">
<value>The stream must support writing.</value>
</data>
<data name="FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat" xml:space="preserve">
<value>The argument '{0}' is invalid. Empty or null formats are not supported.</value>
</data>
</root>

View File

@ -37,10 +37,6 @@ namespace Microsoft.AspNet.Mvc
{
throw new ArgumentNullException(nameof(fileName));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
}
/// <summary>
@ -51,18 +47,13 @@ namespace Microsoft.AspNet.Mvc
/// <param name="fileName">The path to the file. The path must be relative/virtual.</param>
/// <param name="contentType">The Content-Type header of the response.</param>
public VirtualFileResult(string fileName, MediaTypeHeaderValue contentType)
: base(contentType)
: base(contentType?.ToString())
{
if (fileName == null)
{
throw new ArgumentNullException(nameof(fileName));
}
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
FileName = fileName;
}

View File

@ -18,10 +18,10 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
/// </summary>
public class JsonResultExecutor
{
private static readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("application/json")
private static readonly string DefaultContentType = new MediaTypeHeaderValue("application/json")
{
Encoding = Encoding.UTF8
}.CopyAsReadOnly();
}.ToString();
/// <summary>
/// Creates a new <see cref="JsonResultExecutor"/>.

View File

@ -140,7 +140,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
var response = context.HttpContext.Response;
var selectedEncoding = context.ContentType?.Encoding ?? Encoding.UTF8;
var selectedEncoding = MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8;
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
{

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; set; }
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the <see cref="JsonSerializerSettings"/>.

View File

@ -12,6 +12,7 @@ using System.Xml;
using Microsoft.AspNet.Mvc.Formatters.Xml;
using Microsoft.AspNet.Mvc.Formatters.Xml.Internal;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
@ -187,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
}
var writerSettings = WriterSettings.Clone();
writerSettings.Encoding = context.ContentType?.Encoding ?? Encoding.UTF8;
writerSettings.Encoding = MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8;
// Wrap the object only if there is a wrapping type.
var value = context.Object;

View File

@ -12,6 +12,7 @@ using System.Xml.Serialization;
using Microsoft.AspNet.Mvc.Formatters.Xml;
using Microsoft.AspNet.Mvc.Formatters.Xml.Internal;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc.Formatters
{
@ -164,7 +165,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
var response = context.HttpContext.Response;
var writerSettings = WriterSettings.Clone();
writerSettings.Encoding = context.ContentType.Encoding ?? Encoding.UTF8;
writerSettings.Encoding = MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8;
// Wrap the object only if there is a wrapping type.
var value = context.Object;

View File

@ -46,9 +46,9 @@ namespace Microsoft.AspNet.Mvc
public IViewEngine ViewEngine { get; set; }
/// <summary>
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// Gets or sets the Content-Type header for the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; set; }
public string ContentType { get; set; }
/// <inheritdoc />
public override async Task ExecuteResultAsync(ActionContext context)

View File

@ -16,7 +16,6 @@ using Microsoft.AspNet.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Mvc
{
@ -31,9 +30,9 @@ namespace Microsoft.AspNet.Mvc
public object Arguments { get; set; }
/// <summary>
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// Gets or sets the Content-Type header for the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; set; }
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.

View File

@ -23,12 +23,9 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
public class ViewExecutor
{
/// <summary>
/// The default content-type header value for views, <c>text/html; charset=utf8</c>.
/// The default content-type header value for views, <c>text/html; charset=utf-8</c>.
/// </summary>
public static readonly MediaTypeHeaderValue DefaultContentType = new MediaTypeHeaderValue("text/html")
{
Encoding = Encoding.UTF8
}.CopyAsReadOnly();
public static readonly string DefaultContentType = "text/html; charset=utf-8";
/// <summary>
/// Creates a new <see cref="ViewExecutor"/>.
@ -122,7 +119,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
IView view,
ViewDataDictionary viewData,
ITempDataDictionary tempData,
MediaTypeHeaderValue contentType,
string contentType,
int? statusCode)
{
if (actionContext == null)

View File

@ -51,9 +51,9 @@ namespace Microsoft.AspNet.Mvc
public IViewEngine ViewEngine { get; set; }
/// <summary>
/// Gets or sets the <see cref="MediaTypeHeaderValue"/> representing the Content-Type header of the response.
/// Gets or sets the Content-Type header for the response.
/// </summary>
public MediaTypeHeaderValue ContentType { get; set; }
public string ContentType { get; set; }
/// <inheritdoc />
public override async Task ExecuteResultAsync(ActionContext context)

View File

@ -409,10 +409,7 @@ namespace System.Web.Http
}
var result = new JsonResult(content, serializerSettings);
result.ContentType = new MediaTypeHeaderValue("application/json")
{
Encoding = encoding
};
result.ContentType = $"application/json; charset={encoding.WebName}";
return result;
}

View File

@ -18,6 +18,7 @@ using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Routing.Constraints;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -1385,14 +1386,14 @@ namespace Microsoft.AspNet.Mvc.Description
{
public ContentTypeAttribute(string mediaType)
{
ContentTypes.Add(MediaTypeHeaderValue.Parse(mediaType));
ContentTypes.Add(mediaType);
}
public List<MediaTypeHeaderValue> ContentTypes { get; } = new List<MediaTypeHeaderValue>();
public MediaTypeCollection ContentTypes { get; } = new MediaTypeCollection();
public Type Type { get; set; }
public void SetContentTypes(IList<MediaTypeHeaderValue> contentTypes)
public void SetContentTypes(MediaTypeCollection contentTypes)
{
contentTypes.Clear();
foreach (var contentType in ContentTypes)

View File

@ -9,6 +9,8 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -32,7 +34,7 @@ namespace Microsoft.AspNet.Mvc
ContentType = new MediaTypeHeaderValue("text/plain")
{
Encoding = Encoding.UTF7
}
}.ToString()
};
var httpContext = GetHttpContext();
var actionContext = GetActionContext(httpContext);
@ -41,7 +43,7 @@ namespace Microsoft.AspNet.Mvc
await contentResult.ExecuteResultAsync(actionContext);
// Assert
Assert.Equal("text/plain; charset=utf-7", httpContext.Response.ContentType);
MediaTypeAssert.Equal("text/plain; charset=utf-7", httpContext.Response.ContentType);
}
[Fact]
@ -55,7 +57,7 @@ namespace Microsoft.AspNet.Mvc
ContentType = new MediaTypeHeaderValue("text/plain")
{
Encoding = Encoding.ASCII
}
}.ToString()
};
var httpContext = GetHttpContext();
httpContext.Features.Set<IHttpBufferingFeature>(new TestBufferingFeature());
@ -153,7 +155,7 @@ namespace Microsoft.AspNet.Mvc
var contentResult = new ContentResult
{
Content = content,
ContentType = contentType
ContentType = contentType?.ToString()
};
var httpContext = GetHttpContext();
var memoryStream = new MemoryStream();
@ -165,7 +167,8 @@ namespace Microsoft.AspNet.Mvc
await contentResult.ExecuteResultAsync(actionContext);
// Assert
Assert.Equal(expectedContentType, httpContext.Response.ContentType);
var finalResponseContentType = httpContext.Response.ContentType;
Assert.Equal(expectedContentType, finalResponseContentType);
Assert.Equal(expectedContentData, memoryStream.ToArray());
}

View File

@ -10,8 +10,10 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.Net.Http.Headers;
@ -975,7 +977,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Assert
Assert.IsType<ContentResult>(actualContentResult);
Assert.Equal("TestContent", actualContentResult.Content);
Assert.Null(actualContentResult.ContentType.Encoding);
Assert.Null(MediaTypeEncoding.GetEncoding(actualContentResult.ContentType));
Assert.Equal("text/plain", actualContentResult.ContentType.ToString());
}
@ -991,7 +993,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Assert
Assert.IsType<ContentResult>(actualContentResult);
Assert.Equal("TestContent", actualContentResult.Content);
Assert.Same(Encoding.UTF8, actualContentResult.ContentType.Encoding);
Assert.Same(Encoding.UTF8, MediaTypeEncoding.GetEncoding(actualContentResult.ContentType));
Assert.Equal("text/plain; charset=utf-8", actualContentResult.ContentType.ToString());
}
@ -1024,7 +1026,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
Assert.NotNull(contentResult.ContentType);
Assert.Equal(contentType, contentResult.ContentType.ToString());
// The default encoding of ContentResult is used when this result is executed.
Assert.Null(contentResult.ContentType.Encoding);
Assert.Null(MediaTypeEncoding.GetEncoding(contentResult.ContentType));
}
[Fact]
@ -1032,13 +1034,13 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var contentController = new ContentController();
var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value");
var contentType = "text/xml; charset=us-ascii; p1=p1-value";
// Act
var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset_AndEncodingParameter();
// Assert
Assert.Equal(contentType, contentResult.ContentType);
MediaTypeAssert.Equal(contentType, contentResult.ContentType);
}
[Fact]
@ -1046,7 +1048,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var contentController = new ContentController();
var contentType = MediaTypeHeaderValue.Parse("text/xml; charset=us-ascii; p1=p1-value");
var contentType = "text/xml; charset=us-ascii; p1=p1-value";
// Act
var contentResult = (ContentResult)contentController.Content_WithEncodingInCharset();

View File

@ -7,11 +7,11 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNet.Mvc
@ -37,14 +37,14 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var fileContents = new byte[0];
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var expectedMediaType = MediaTypeHeaderValue.Parse(contentType);
var expectedMediaType = contentType;
// Act
var result = new FileContentResult(fileContents, contentType);
// Assert
Assert.Same(fileContents, result.FileContents);
Assert.Equal(expectedMediaType, result.ContentType);
MediaTypeAssert.Equal(expectedMediaType, result.ContentType);
}
[Fact]
@ -83,7 +83,7 @@ namespace Microsoft.AspNet.Mvc
var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var result = new FileContentResult(buffer, MediaTypeHeaderValue.Parse(expectedContentType));
var result = new FileContentResult(buffer, expectedContentType);
// Act
await result.ExecuteResultAsync(context);
@ -108,7 +108,7 @@ namespace Microsoft.AspNet.Mvc
var context = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var result = new FileContentResult(buffer, MediaTypeHeaderValue.Parse(expectedContentType));
var result = new FileContentResult(buffer, expectedContentType);
// Act
await result.ExecuteResultAsync(context);

View File

@ -274,12 +274,12 @@ namespace Microsoft.AspNet.Mvc
public bool WasWriteFileCalled;
public EmptyFileResult()
: base(MediaTypeHeaderValue.Parse("application/octet"))
: base("application/octet")
{
}
public EmptyFileResult(string contentType)
: base(MediaTypeHeaderValue.Parse(contentType))
: base(contentType)
{
}

View File

@ -10,11 +10,11 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -41,14 +41,14 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var stream = Stream.Null;
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var expectedMediaType = MediaTypeHeaderValue.Parse(contentType);
var expectedMediaType = contentType;
// Act
var result = new FileStreamResult(stream, contentType);
// Assert
Assert.Equal(stream, result.FileStream);
Assert.Equal(expectedMediaType, result.ContentType);
MediaTypeAssert.Equal(expectedMediaType, result.ContentType);
}
[Fact]
@ -130,7 +130,7 @@ namespace Microsoft.AspNet.Mvc
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var result = new FileStreamResult(originalStream, MediaTypeHeaderValue.Parse(expectedContentType));
var result = new FileStreamResult(originalStream, expectedContentType);
// Act
await result.ExecuteResultAsync(actionContext);
@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Mvc
httpContext.Response.Body = outStream;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var result = new FileStreamResult(originalStream, MediaTypeHeaderValue.Parse(expectedContentType));
var result = new FileStreamResult(originalStream, expectedContentType);
// Act
await result.ExecuteResultAsync(actionContext);

View File

@ -4,9 +4,10 @@
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -32,7 +33,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
string contentType)
{
// Arrange
var mediaType = MediaTypeHeaderValue.Parse("application/json");
var mediaType = new StringSegment("application/json");
var mockObjects = new MockObjects(format, place);
var resultExecutingContext = mockObjects.CreateResultExecutingContext();
@ -52,7 +53,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Assert
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
Assert.Equal(1, objectResult.ContentTypes.Count);
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
MediaTypeAssert.Equal(mediaType, objectResult.ContentTypes[0]);
}
[Fact]
@ -61,7 +62,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// If the format is present in both route and query data, the one in route data wins
// Arrange
var mediaType = MediaTypeHeaderValue.Parse("application/json");
var mediaType = new StringSegment("application/json");
var mockObjects = new MockObjects("json", FormatSource.RouteData);
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.Response).Returns(new Mock<HttpResponse>().Object);
@ -95,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Assert
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
Assert.Equal(1, objectResult.ContentTypes.Count);
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
MediaTypeAssert.Equal(mediaType, objectResult.ContentTypes[0]);
}
[Theory]
@ -108,7 +109,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
string contentType)
{
// Arrange
var mediaType = MediaTypeHeaderValue.Parse(contentType);
var mediaType = new StringSegment(contentType);
var mockObjects = new MockObjects(format, place);
var resultExecutingContext = mockObjects.CreateResultExecutingContext();
@ -127,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Assert
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
Assert.Equal(1, objectResult.ContentTypes.Count);
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
MediaTypeAssert.Equal(mediaType, objectResult.ContentTypes[0]);
}
[Theory]
@ -303,7 +304,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
public void FormatFilter_ExplicitContentType_SetOnObjectResult_TakesPrecedence()
{
// Arrange
var mediaType = MediaTypeHeaderValue.Parse("application/foo");
var mediaType = new StringSegment("application/foo");
var mockObjects = new MockObjects("json", FormatSource.QueryData);
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.Response).Returns(new Mock<HttpResponse>().Object);
@ -330,7 +331,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Assert
var result = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
Assert.Equal(1, result.ContentTypes.Count);
AssertMediaTypesEqual(mediaType, result.ContentTypes[0]);
MediaTypeAssert.Equal(mediaType, result.ContentTypes[0]);
}
[Fact]
@ -366,21 +367,6 @@ namespace Microsoft.AspNet.Mvc.Formatters
Assert.Equal(0, result.ContentTypes.Count);
}
private static void AssertMediaTypesEqual(
MediaTypeHeaderValue expectedMediaType,
MediaTypeHeaderValue actualMediaType)
{
Assert.Equal(expectedMediaType.MediaType, actualMediaType.MediaType);
Assert.Equal(expectedMediaType.SubType, actualMediaType.SubType);
Assert.Equal(expectedMediaType.Charset, actualMediaType.Charset);
Assert.Equal(expectedMediaType.MatchesAllTypes, actualMediaType.MatchesAllTypes);
Assert.Equal(expectedMediaType.MatchesAllSubTypes, actualMediaType.MatchesAllSubTypes);
Assert.Equal(expectedMediaType.Parameters.Count, actualMediaType.Parameters.Count);
foreach (var item in expectedMediaType.Parameters)
{
Assert.Equal(item.Value, NameValueHeaderValue.Find(actualMediaType.Parameters, item.Name).Value);
}
}
private class MockObjects
{

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -10,33 +12,44 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
public class FormatterMappingsTest
{
[Theory]
[InlineData(null)]
[InlineData("")]
public void FormatterMappings_GetMediaTypeMappingForFormat_ThrowsForInvalidFormats(string format)
{
// Arrange
var options = new FormatterMappings();
// Act & Assert
Assert.Throws<ArgumentException>("format", () => options.GetMediaTypeMappingForFormat(format));
}
[Theory]
[InlineData(".xml", "application/xml", "xml")]
[InlineData("json", "application/json", "JSON")]
[InlineData(".foo", "text/foo", "Foo")]
[InlineData(".Json", "application/json", "json")]
[InlineData("FOo", "text/foo", "FOO")]
[InlineData("FOo", "text/foo", "FOO")]
public void FormatterMappings_SetFormatMapping_DiffSetGetFormat(string setFormat, string contentType, string getFormat)
{
// Arrange
var mediaType = MediaTypeHeaderValue.Parse(contentType);
var options = new FormatterMappings();
options.SetMediaTypeMappingForFormat(setFormat, mediaType);
options.SetMediaTypeMappingForFormat(setFormat, MediaTypeHeaderValue.Parse(contentType));
// Act
var returnMediaType = options.GetMediaTypeMappingForFormat(getFormat);
// Assert
Assert.Equal(mediaType, returnMediaType);
MediaTypeAssert.Equal(contentType, returnMediaType);
}
[Fact]
public void FormatterMappings_Invalid_Period()
{
// Arrange
var options = new FormatterMappings();
var format = ".";
var expected = string.Format(@"The format provided is invalid '{0}'. A format must be a non-empty file-" +
var expected = string.Format(@"The format provided is invalid '{0}'. A format must be a non-empty file-" +
"extension, optionally prefixed with a '.' character.", format);
// Act and assert
@ -70,12 +83,12 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
// Arrange
var options = new FormatterMappings();
var expected = string.Format(@"The media type ""{0}"" is not valid. MediaTypes containing wildcards (*) " +
var expected = string.Format(@"The media type ""{0}"" is not valid. MediaTypes containing wildcards (*) " +
"are not allowed in formatter mappings.", format);
// Act and assert
var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat(
"star",
"star",
MediaTypeHeaderValue.Parse(format)));
Assert.Equal(expected, exception.Message);
}

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.AspNet.Mvc.Formatters
{
public class MediaTypeComparisonsTest
{
[Theory]
[InlineData("application/json", "application/json", true)]
[InlineData("application/json", "application/json;charset=utf-8", true)]
[InlineData("application/json;charset=utf-8", "application/json", false)]
[InlineData("application/json;q=0.8", "application/json;q=0.9", true)]
[InlineData("application/json;q=0.8;charset=utf-7", "application/json;charset=utf-8;q=0.9", true)]
[InlineData("application/json;format=indent;charset=utf-8", "application/json", false)]
[InlineData("application/json", "application/json;format=indent;charset=utf-8", true)]
[InlineData("application/json;format=indent;charset=utf-8", "application/json;format=indent;charset=utf-8", true)]
[InlineData("application/json;charset=utf-8;format=indent", "application/json;format=indent;charset=utf-8", true)]
public void IsSubsetOf(string set, string subset, bool expectedResult)
{
// Arrange & Act
var result = MediaTypeComparisons.IsSubsetOf(
new StringSegment(set),
new StringSegment(subset));
// Assert
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("*/*", true)]
[InlineData("text/*", false)]
[InlineData("text/plain", false)]
public void MatchesAllTypes(string value, bool expectedResult)
{
// Arrange
var mediaType = new StringSegment(value);
// Act
var result = MediaTypeComparisons.MatchesAllTypes(mediaType);
// Assert
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("*/*", true)]
[InlineData("text/*", true)]
[InlineData("text/plain", false)]
public void MatchesAllSubtypes(string value, bool expectedResult)
{
// Arrange
var mediaType = new StringSegment(value);
// Act
var result = MediaTypeComparisons.MatchesAllSubtypes(mediaType);
// Assert
Assert.Equal(expectedResult, result);
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -41,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
// Arrange
var type = declaredTypeAsString ? typeof(string) : typeof(object);
var contentType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null;
var contentType = useNonNullContentType ? new StringSegment("text/plain") : new StringSegment();
var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
@ -73,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
declaredType,
"Something non null.")
{
ContentType = MediaTypeHeaderValue.Parse("text/plain"),
ContentType = new StringSegment("text/plain"),
};
var formatter = new HttpNoContentOutputFormatter();
@ -101,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
typeof(string),
value)
{
ContentType = MediaTypeHeaderValue.Parse("text/plain"),
ContentType = new StringSegment("text/plain"),
};
var formatter = new HttpNoContentOutputFormatter()

View File

@ -7,8 +7,10 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Moq.Protected;
using Xunit;
namespace Microsoft.AspNet.Mvc.Formatters
@ -59,7 +61,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
typeof(string),
"someValue")
{
ContentType = MediaTypeHeaderValue.Parse(httpRequest.Headers[HeaderNames.Accept]),
ContentType = new StringSegment(httpRequest.Headers[HeaderNames.Accept]),
};
// Act
@ -69,17 +71,47 @@ namespace Microsoft.AspNet.Mvc.Formatters
Assert.Equal(Encoding.GetEncoding(expectedEncoding), actualEncoding);
}
[Theory]
[InlineData("application/json; charset=utf-16", "application/json; charset=utf-32")]
[InlineData("application/json; charset=utf-16; format=indent", "application/json; charset=utf-32; format=indent")]
public void WriteResponse_OverridesCharset_IfDifferentFromContentTypeCharset(
string contentType,
string expectedContentType)
{
// Arrange
var formatter = new Mock<OutputFormatter>();
formatter
.Setup(f => f.SelectCharacterEncoding(It.IsAny<OutputFormatterWriteContext>()))
.Returns(Encoding.UTF32);
var formatterContext = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null)
{
ContentType = new StringSegment(contentType),
};
// Act
formatter.Object.WriteAsync(formatterContext);
// Assert
Assert.Equal(new StringSegment(expectedContentType), formatterContext.ContentType);
}
[Fact]
public void WriteResponseContentHeaders_NoSupportedEncodings_NoEncodingIsSet()
{
// Arrange
var formatter = new TestOutputFormatter();
var testContentType = MediaTypeHeaderValue.Parse("text/json");
var testContentType = new StringSegment("text/json");
formatter.SupportedEncodings.Clear();
formatter.SupportedMediaTypes.Clear();
formatter.SupportedMediaTypes.Add(testContentType);
formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
@ -94,11 +126,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.WriteResponseHeaders(context);
// Assert
Assert.Null(context.ContentType.Encoding);
Assert.Null(MediaTypeHeaderValue.Parse(context.ContentType.Value).Encoding);
Assert.Equal(testContentType, context.ContentType);
// If we had set an encoding, it would be part of the content type header
Assert.Equal(testContentType, context.HttpContext.Response.GetTypedHeaders().ContentType);
Assert.Equal(MediaTypeHeaderValue.Parse(testContentType.Value), context.HttpContext.Response.GetTypedHeaders().ContentType);
}
[Fact]
@ -154,7 +186,9 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedTypes.Add(typeof(int));
// Act
var contentTypes = formatter.GetSupportedContentTypes(contentType: null, objectType: typeof(string));
var contentTypes = formatter.GetSupportedContentTypes(
contentType: null,
objectType: typeof(string));
// Assert
Assert.Null(contentTypes);
@ -174,7 +208,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
typeof(string),
"Hello, world!")
{
ContentType = formatter.SupportedMediaTypes[0],
ContentType = new StringSegment(formatter.SupportedMediaTypes[0].ToString()),
};
// Act
@ -194,7 +228,9 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
// Act
var contentTypes = formatter.GetSupportedContentTypes(contentType: null, objectType: typeof(string));
var contentTypes = formatter.GetSupportedContentTypes(
contentType: null,
objectType: typeof(string));
// Assert
Assert.Equal(2, contentTypes.Count);
@ -214,7 +250,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Act
var contentTypes = formatter.GetSupportedContentTypes(
MediaTypeHeaderValue.Parse("application/*"),
"application/*",
typeof(int));
// Assert
@ -234,7 +270,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Act
var contentTypes = formatter.GetSupportedContentTypes(
MediaTypeHeaderValue.Parse("application/xml"),
"application/xml",
typeof(int));
// Assert
@ -251,7 +287,9 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedMediaTypes.Clear();
// Act
var contentTypes = formatter.GetSupportedContentTypes(contentType: null, objectType: typeof(int));
var contentTypes = formatter.GetSupportedContentTypes(
contentType: null,
objectType: typeof(int));
// Assert
Assert.Null(contentTypes);

View File

@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -21,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
// Arrange
var formatter = new StreamOutputFormatter();
var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType);
var contentTypeHeader = new StringSegment(contentType);
var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
@ -46,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
{
// Arrange
var formatter = new StreamOutputFormatter();
var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType);
var contentTypeHeader = contentType == null ? new StringSegment() : new StringSegment(contentType);
var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),

View File

@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Net.Http.Headers;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;
@ -35,9 +35,9 @@ namespace Microsoft.AspNet.Mvc.Formatters
bool expectedCanWriteResult)
{
// Arrange
var expectedContentType = expectedCanWriteResult ?
MediaTypeHeaderValue.Parse("text/plain") :
MediaTypeHeaderValue.Parse("application/json");
var expectedContentType = expectedCanWriteResult ?
new StringSegment("text/plain") :
new StringSegment("application/json");
var formatter = new StringOutputFormatter();
var type = useDeclaredTypeAsString ? typeof(string) : typeof(object);
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
value);
context.ContentType = MediaTypeHeaderValue.Parse("application/json");
context.ContentType = new StringSegment("application/json");
// Act
var result = formatter.CanWriteResult(context);

View File

@ -9,10 +9,12 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -43,12 +45,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Act
var formatter = executor.SelectFormatter(
context,
new[] { new MediaTypeHeaderValue("application/json") },
new MediaTypeCollection { "application/json" },
formatters);
// Assert
Assert.Same(formatters[1], formatter);
Assert.Equal(new MediaTypeHeaderValue("application/json"), context.ContentType);
MediaTypeAssert.Equal("application/json", context.ContentType);
}
// For this test case probably the most common use case is when there is a format mapping based
@ -72,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Assert
Assert.IsType<TestStringOutputFormatter>(executor.SelectedOutputFormatter);
Assert.Equal("text/plain; charset=utf-8", httpContext.Response.ContentType);
MediaTypeAssert.Equal("text/plain; charset=utf-8", httpContext.Response.ContentType);
}
[Fact]
@ -98,12 +100,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Act
var formatter = executor.SelectFormatter(
context,
new[] { new MediaTypeHeaderValue("application/json") },
new MediaTypeCollection { "application/json" },
formatters);
// Assert
Assert.Same(formatters[1], formatter);
Assert.Equal(new MediaTypeHeaderValue("application/json"), context.ContentType);
Assert.Equal(new StringSegment("application/json"), context.ContentType);
}
[Fact]
@ -149,7 +151,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Act
var formatter = executor.SelectFormatter(
context,
new[] { new MediaTypeHeaderValue("application/json") },
new MediaTypeCollection { "application/json" },
formatters);
// Assert
@ -176,18 +178,18 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
}
// ObjectResult.ContentTypes, Accept header, expected content type
public static TheoryData<string[], string, string> ContentTypes
public static TheoryData<MediaTypeCollection, string, string> ContentTypes
{
get
{
var contentTypes = new string[]
var contentTypes = new MediaTypeCollection
{
"text/plain",
"text/xml",
"application/json",
};
return new TheoryData<string[], string, string>()
return new TheoryData<MediaTypeCollection, string, string>()
{
// Empty accept header, should select based on ObjectResult.ContentTypes.
{ contentTypes, "", "application/json" },
@ -216,7 +218,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
[Theory]
[MemberData(nameof(ContentTypes))]
public void SelectFormatter_WithMultipleProvidedContentTypes_DoesConneg(
IEnumerable<string> contentTypes,
MediaTypeCollection contentTypes,
string acceptHeader,
string expectedContentType)
{
@ -240,12 +242,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Act
var formatter = executor.SelectFormatter(
context,
contentTypes.Select(contentType => MediaTypeHeaderValue.Parse(contentType)).ToList(),
contentTypes,
formatters);
// Assert
Assert.Same(formatters[1], formatter);
Assert.Equal(new MediaTypeHeaderValue(expectedContentType), context.ContentType);
Assert.Equal(new StringSegment(expectedContentType), context.ContentType);
}
[Fact]
@ -270,12 +272,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Act
var formatter = executor.SelectFormatter(
context,
new List<MediaTypeHeaderValue>(),
new MediaTypeCollection(),
formatters);
// Assert
Assert.Same(formatters[1], formatter);
Assert.Equal(new MediaTypeHeaderValue("application/json"), context.ContentType);
Assert.Equal(new StringSegment("application/json"), context.ContentType);
Assert.Null(context.FailedContentNegotiation);
}
@ -297,17 +299,17 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = "text/custom, application/custom";
context.HttpContext.Request.Headers[HeaderNames.Accept] = "text/custom,application/custom";
// Act
var formatter = executor.SelectFormatter(
context,
new MediaTypeHeaderValue[] { },
new MediaTypeCollection { },
formatters);
// Assert
Assert.Same(formatters[0], formatter);
Assert.Equal(new MediaTypeHeaderValue("application/xml"), context.ContentType);
Assert.Equal(new StringSegment("application/xml"), context.ContentType);
Assert.True(context.FailedContentNegotiation);
}
@ -373,9 +375,14 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
{
// Arrange
var result = new ObjectResult("input");
result.ContentTypes = contentTypes
.Select(contentType => MediaTypeHeaderValue.Parse(contentType))
.ToList();
var mediaTypes = new MediaTypeCollection();
foreach (var contentType in contentTypes)
{
mediaTypes.Add(contentType);
}
result.ContentTypes = mediaTypes;
var executor = CreateExecutor();
@ -395,7 +402,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Chrome & Opera
[InlineData("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "application/json; charset=utf-8")]
// IE
[InlineData("text/html, application/xhtml+xml, */*", "application/json; charset=utf-8")]
[InlineData("text/html,application/xhtml+xml,*/*", "application/json; charset=utf-8")]
// Firefox & Safari
[InlineData("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "application/json; charset=utf-8")]
// Misc
@ -432,7 +439,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
// Chrome & Opera
[InlineData("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "application/xml; charset=utf-8")]
// IE
[InlineData("text/html, application/xhtml+xml, */*", "application/json; charset=utf-8")]
[InlineData("text/html,application/xhtml+xml,*/*", "application/json; charset=utf-8")]
// Firefox & Safari
[InlineData("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "application/xml; charset=utf-8")]
// Misc
@ -462,7 +469,8 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
await executor.ExecuteAsync(actionContext, result);
// Assert
Assert.Equal(expectedContentType, actionContext.HttpContext.Response.Headers[HeaderNames.ContentType]);
var responseContentType = actionContext.HttpContext.Response.Headers[HeaderNames.ContentType];
MediaTypeAssert.Equal(expectedContentType, responseContentType);
}
private static IServiceCollection CreateServices()
@ -572,7 +580,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new public IOutputFormatter SelectFormatter(
OutputFormatterWriteContext formatterContext,
IList<MediaTypeHeaderValue> contentTypes,
MediaTypeCollection contentTypes,
IList<IOutputFormatter> formatters)
{
return base.SelectFormatter(formatterContext, contentTypes, formatters);
@ -593,7 +601,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
protected override IOutputFormatter SelectFormatter(
OutputFormatterWriteContext formatterContext,
IList<MediaTypeHeaderValue> contentTypes,
MediaTypeCollection contentTypes,
IList<IOutputFormatter> formatters)
{
SelectedOutputFormatter = base.SelectFormatter(formatterContext, contentTypes, formatters);

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -102,13 +104,13 @@ namespace Microsoft.AspNet.Mvc.Internal
string expectedContentType)
{
// Arrange
var defaultContentType = MediaTypeHeaderValue.Parse("text/default; p1=p1-value; charset=utf-8");
var defaultContentType = "text/default; p1=p1-value; charset=utf-8";
// Act
string resolvedContentType = null;
Encoding resolvedContentTypeEncoding = null;
ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentType,
contentType?.ToString(),
responseContentType,
defaultContentType,
out resolvedContentType,
@ -123,7 +125,7 @@ namespace Microsoft.AspNet.Mvc.Internal
{
// Arrange
var expectedContentType = "invalid-content-type";
var defaultContentType = MediaTypeHeaderValue.Parse("text/plain; charset=utf-8");
var defaultContentType = "text/plain; charset=utf-8";
// Act
string resolvedContentType = null;
@ -136,6 +138,7 @@ namespace Microsoft.AspNet.Mvc.Internal
out resolvedContentTypeEncoding);
// Assert
Assert.Equal(expectedContentType, resolvedContentType);
Assert.Equal(Encoding.UTF8, resolvedContentTypeEncoding);
}

View File

@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNet.Mvc.Formatters
{
public class MediaTypeCollectionTest
{
[Fact]
public void Add_MediaTypeHeaderValue_AddsTheStringSegmentRepresentationOfTheMediaType()
{
// Arrange
var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse("application/json;charset=utf-16");
var collection = new MediaTypeCollection();
// Act
collection.Add(mediaTypeHeaderValue);
// Assert
Assert.Contains("application/json; charset=utf-16", collection);
}
[Fact]
public void Insert_MediaTypeHeaderValue_AddsTheStringSegmentRepresentationOfTheMediaTypeOnTheGivenIndex()
{
// Arrange
var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse("application/json;charset=utf-16");
var collection = new MediaTypeCollection
{
MediaTypeHeaderValue.Parse("text/plain"),
MediaTypeHeaderValue.Parse("text/xml")
};
// Act
collection.Insert(1, mediaTypeHeaderValue);
// Assert
Assert.Equal(1, collection.IndexOf("application/json; charset=utf-16"));
}
[Fact]
public void Remove_MediaTypeHeaderValue_RemovesTheStringSegmentRepresentationOfTheMediaType()
{
// Arrange
var collection = new MediaTypeCollection
{
MediaTypeHeaderValue.Parse("text/plain"),
MediaTypeHeaderValue.Parse("text/xml")
};
// Act
collection.Remove(MediaTypeHeaderValue.Parse("text/xml"));
// Assert
Assert.DoesNotContain("text/xml", collection);
}
}
}

View File

@ -10,11 +10,11 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -41,14 +41,14 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var expectedMediaType = MediaTypeHeaderValue.Parse(contentType);
var expectedMediaType = contentType;
// Act
var result = new PhysicalFileResult(path, contentType);
// Assert
Assert.Equal(path, result.FileName);
Assert.Equal(expectedMediaType, result.ContentType);
MediaTypeAssert.Equal(expectedMediaType, result.ContentType);
}
[Fact]
@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var expectedContentType = "text/foo; charset=us-ascii";
var path = Path.GetFullPath(Path.Combine(".", "TestFiles", "FilePathResultTestFile_ASCII.txt"));
var result = new TestPhysicalFileResult(path, MediaTypeHeaderValue.Parse(expectedContentType))
var result = new TestPhysicalFileResult(path, expectedContentType)
{
IsAscii = true
};
@ -205,11 +205,6 @@ namespace Microsoft.AspNet.Mvc
{
}
public TestPhysicalFileResult(string filePath, MediaTypeHeaderValue contentType)
: base(filePath, contentType)
{
}
public bool IsAscii { get; set; } = false;
protected override Stream GetFileStream(string path)

View File

@ -8,7 +8,9 @@ using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -21,8 +23,8 @@ namespace Microsoft.AspNet.Mvc.Test
public async Task ProducesAttribute_SetsContentType()
{
// Arrange
var mediaType1 = MediaTypeHeaderValue.Parse("application/json");
var mediaType2 = MediaTypeHeaderValue.Parse("text/json;charset=utf-8");
var mediaType1 = new StringSegment("application/json");
var mediaType2 = new StringSegment("text/json;charset=utf-8");
var producesContentAttribute = new ProducesAttribute("application/json", "text/json;charset=utf-8");
var resultExecutingContext = CreateResultExecutingContext(new IFilterMetadata[] { producesContentAttribute });
var next = new ResultExecutionDelegate(
@ -34,8 +36,8 @@ namespace Microsoft.AspNet.Mvc.Test
// Assert
var objectResult = resultExecutingContext.Result as ObjectResult;
Assert.Equal(2, objectResult.ContentTypes.Count);
ValidateMediaType(mediaType1, objectResult.ContentTypes[0]);
ValidateMediaType(mediaType2, objectResult.ContentTypes[1]);
MediaTypeAssert.Equal(mediaType1, objectResult.ContentTypes[0]);
MediaTypeAssert.Equal(mediaType2, objectResult.ContentTypes[1]);
}
[Fact]
@ -153,20 +155,6 @@ namespace Microsoft.AspNet.Mvc.Test
Assert.Empty(producesAttribute.ContentTypes);
}
private static void ValidateMediaType(MediaTypeHeaderValue expectedMediaType, MediaTypeHeaderValue actualMediaType)
{
Assert.Equal(expectedMediaType.MediaType, actualMediaType.MediaType);
Assert.Equal(expectedMediaType.SubType, actualMediaType.SubType);
Assert.Equal(expectedMediaType.Charset, actualMediaType.Charset);
Assert.Equal(expectedMediaType.MatchesAllTypes, actualMediaType.MatchesAllTypes);
Assert.Equal(expectedMediaType.MatchesAllSubTypes, actualMediaType.MatchesAllSubTypes);
Assert.Equal(expectedMediaType.Parameters.Count, actualMediaType.Parameters.Count);
foreach (var item in expectedMediaType.Parameters)
{
Assert.Equal(item.Value, NameValueHeaderValue.Find(actualMediaType.Parameters, item.Name).Value);
}
}
private static ResultExecutedContext CreateResultExecutedContext(ResultExecutingContext context)
{
return new ResultExecutedContext(context, context.Filters, context.Result, context.Controller);

View File

@ -11,11 +11,11 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -42,14 +42,14 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var path = Path.GetFullPath("helllo.txt");
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
var expectedMediaType = MediaTypeHeaderValue.Parse(contentType);
var expectedMediaType = contentType;
// Act
var result = new VirtualFileResult(path, contentType);
// Assert
Assert.Equal(path, result.FileName);
Assert.Equal(expectedMediaType, result.ContentType);
MediaTypeAssert.Equal(expectedMediaType, result.ContentType);
}
[Fact]
@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Mvc
// Arrange
var expectedContentType = "text/foo; charset=us-ascii";
var result = new TestVirtualFileResult(
"FilePathResultTestFile_ASCII.txt", MediaTypeHeaderValue.Parse(expectedContentType))
"FilePathResultTestFile_ASCII.txt", expectedContentType)
{
FileProvider = GetFileProvider("FilePathResultTestFile_ASCII.txt"),
IsAscii = true,
@ -347,11 +347,6 @@ namespace Microsoft.AspNet.Mvc
{
}
public TestVirtualFileResult(string filePath, MediaTypeHeaderValue contentType)
: base(filePath, contentType)
{
}
public bool IsAscii { get; set; } = false;
protected override Stream GetFileStream(IFileInfo fileInfo)

View File

@ -50,7 +50,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
var context = GetActionContext();
var result = new JsonResult(new { foo = "abcd" });
result.ContentType = new MediaTypeHeaderValue("text/json");
result.ContentType = "text/json";
var executor = CreateExcutor();
// Act
@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
result.ContentType = new MediaTypeHeaderValue("text/json")
{
Encoding = Encoding.ASCII
};
}.ToString();
var executor = CreateExcutor();
// Act

View File

@ -14,6 +14,7 @@ using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Newtonsoft.Json;
@ -262,7 +263,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
typeof(string),
content)
{
ContentType = mediaType,
ContentType = new StringSegment(mediaType.ToString()),
};
// Act
@ -313,7 +314,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
outputType,
outputValue)
{
ContentType = mediaTypeHeaderValue,
ContentType = new StringSegment(contentType),
};
}

View File

@ -12,6 +12,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Formatters.Xml.Internal;
using Microsoft.AspNet.Testing.xunit;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -123,7 +124,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
var formatter = new TestXmlDataContractSerializerOutputFormatter();
var context = GetOutputFormatterContext(input, typeof(DummyClass));
context.ContentType = MediaTypeHeaderValue.Parse("application/xml");
context.ContentType = new StringSegment("application/xml");
// Act
formatter.CanWriteResult(context);
@ -382,7 +383,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
// Arrange
var formatter = new XmlDataContractSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(input, declaredType);
outputFormatterContext.ContentType = MediaTypeHeaderValue.Parse("application/xml");
outputFormatterContext.ContentType = new StringSegment("application/xml");
// Act
var result = formatter.CanWriteResult(outputFormatterContext);
@ -412,7 +413,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
// Act
var result = formatter.GetSupportedContentTypes(
MediaTypeHeaderValue.Parse("application/xml"),
"application/xml",
type);
// Assert

View File

@ -11,6 +11,7 @@ using System.Xml.Serialization;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Formatters.Xml.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Moq;
using Xunit;
@ -76,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
var formatter = new TestXmlSerializerOutputFormatter();
var context = GetOutputFormatterContext(input, typeof(DummyClass));
context.ContentType = MediaTypeHeaderValue.Parse("application/xml");
context.ContentType = new StringSegment("application/xml");
// Act
formatter.CanWriteResult(context);
@ -300,7 +301,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
// Arrange
var formatter = new XmlSerializerOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(input, declaredType);
outputFormatterContext.ContentType = MediaTypeHeaderValue.Parse("application/xml");
outputFormatterContext.ContentType = new StringSegment("application/xml");
// Act
var result = formatter.CanWriteResult(outputFormatterContext);
@ -342,7 +343,7 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
var formatter = new XmlSerializerOutputFormatter();
// Act
var result = formatter.GetSupportedContentTypes(MediaTypeHeaderValue.Parse("application/xml"), type);
var result = formatter.GetSupportedContentTypes("application/xml", type);
// Assert
if (expectedOutput != null)

View File

@ -0,0 +1,47 @@
using System;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit.Sdk;
namespace Microsoft.AspNet.Mvc.TestCommon
{
public class MediaTypeAssert
{
public static void Equal(string left, string right)
{
Equal(new StringSegment(left), new StringSegment(right));
}
public static void Equal(string left, StringSegment right)
{
Equal(new StringSegment(left), right);
}
public static void Equal(StringSegment left, string right)
{
Equal(left, new StringSegment(right));
}
public static void Equal(StringSegment left, StringSegment right)
{
if (!left.HasValue && !right.HasValue)
{
return;
}
else if (!left.HasValue || !right.HasValue)
{
throw new EqualException(left.ToString(), right.ToString());
}
MediaTypeHeaderValue leftMediaType = null;
MediaTypeHeaderValue rightMediaType = null;
if (!MediaTypeHeaderValue.TryParse(left.Value, out leftMediaType) ||
!MediaTypeHeaderValue.TryParse(right.Value, out rightMediaType) ||
!leftMediaType.Equals(rightMediaType))
{
throw new EqualException(left.ToString(), right.ToString());
}
}
}
}

View File

@ -8,6 +8,7 @@ using System.Text;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Routing;
using Moq;
using Newtonsoft.Json;
@ -90,7 +91,7 @@ namespace Microsoft.AspNet.Mvc
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal(content, contentResult.Content);
Assert.Equal("text/asp; charset=us-ascii", contentResult.ContentType.ToString());
Assert.Equal(encoding, contentResult.ContentType.Encoding);
Assert.Equal(encoding, MediaTypeEncoding.GetEncoding(contentResult.ContentType));
}
[Theory]

View File

@ -12,9 +12,11 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Mvc.ViewFeatures.Buffer;
@ -367,26 +369,26 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(404, actionContext.HttpContext.Response.StatusCode);
}
public static TheoryData<MediaTypeHeaderValue, string> ViewComponentResultContentTypeData
public static TheoryData<string, string> ViewComponentResultContentTypeData
{
get
{
return new TheoryData<MediaTypeHeaderValue, string>
return new TheoryData<string, string>
{
{
null,
"text/html; charset=utf-8"
},
{
new MediaTypeHeaderValue("text/foo"),
"text/foo",
"text/foo"
},
{
MediaTypeHeaderValue.Parse("text/foo;p1=p1-value"),
"text/foo;p1=p1-value",
"text/foo; p1=p1-value"
},
{
new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII },
new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII }.ToString(),
"text/foo; charset=us-ascii"
}
};
@ -396,8 +398,8 @@ namespace Microsoft.AspNet.Mvc
[Theory]
[MemberData(nameof(ViewComponentResultContentTypeData))]
public async Task ViewComponentResult_SetsContentTypeHeader(
MediaTypeHeaderValue contentType,
string expectedContentTypeHeaderValue)
string contentType,
string expectedContentType)
{
// Arrange
var descriptor = new ViewComponentDescriptor()
@ -424,13 +426,14 @@ namespace Microsoft.AspNet.Mvc
await viewComponentResult.ExecuteResultAsync(actionContext);
// Assert
Assert.Equal(expectedContentTypeHeaderValue, actionContext.HttpContext.Response.ContentType);
var resultContentType = actionContext.HttpContext.Response.ContentType;
MediaTypeAssert.Equal(expectedContentType, resultContentType);
// Check if the original instance provided by the user has not changed.
// Since we do not have access to the new instance created within the view executor,
// check if at least the content is the same.
var contentTypeAfterViewResultExecution = contentType?.ToString();
Assert.Equal(contentTypeBeforeViewResultExecution, contentTypeAfterViewResultExecution);
MediaTypeAssert.Equal(contentTypeBeforeViewResultExecution, contentTypeAfterViewResultExecution);
}
[Fact]
@ -454,7 +457,7 @@ namespace Microsoft.AspNet.Mvc
{
Arguments = new { name = "World!" },
ViewComponentName = "Text",
ContentType = new MediaTypeHeaderValue("text/html") { Encoding = Encoding.UTF8 },
ContentType = new MediaTypeHeaderValue("text/html") { Encoding = Encoding.UTF8 }.ToString(),
TempData = _tempDataDictionary,
};

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Routing;
@ -262,7 +263,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var context = GetActionContext();
var executor = GetViewExecutor();
var contentType = MediaTypeHeaderValue.Parse("application/x-my-content-type");
var contentType = "application/x-my-content-type";
var viewResult = new PartialViewResult
{
@ -281,7 +282,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
// Check if the original instance provided by the user has not changed.
// Since we do not have access to the new instance created within the view executor,
// check if at least the content is the same.
Assert.Null(contentType.Encoding);
Assert.Null(MediaTypeEncoding.GetEncoding(contentType));
}
[Fact]

View File

@ -10,8 +10,10 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Routing;
using Microsoft.Extensions.DependencyInjection;
@ -115,11 +117,11 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
view,
viewData,
Mock.Of<ITempDataDictionary>(),
contentType,
contentType?.ToString(),
statusCode: null);
// Assert
Assert.Equal(expectedContentType, context.Response.ContentType);
MediaTypeAssert.Equal(expectedContentType, context.Response.ContentType);
Assert.Equal("abcd", Encoding.UTF8.GetString(memoryStream.ToArray()));
}

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Routing;
@ -258,7 +259,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var context = GetActionContext();
var executor = GetViewExecutor();
var contentType = MediaTypeHeaderValue.Parse("application/x-my-content-type");
var contentType = "application/x-my-content-type";
var viewResult = new ViewResult
{
@ -273,11 +274,6 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
// Assert
Assert.Equal("application/x-my-content-type", context.HttpContext.Response.ContentType);
// Check if the original instance provided by the user has not changed.
// Since we do not have access to the new instance created within the view executor,
// check if at least the content is the same.
Assert.Null(contentType.Encoding);
}
[Fact]

View File

@ -10,6 +10,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.Formatters;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Routing;
using Newtonsoft.Json;
@ -264,7 +265,7 @@ namespace System.Web.Http
var jsonResult = Assert.IsType<JsonResult>(result);
Assert.Same(product, jsonResult.Value);
Assert.Same(Encoding.UTF8, jsonResult.ContentType.Encoding);
Assert.Same(Encoding.UTF8, MediaTypeEncoding.GetEncoding(jsonResult.ContentType));
}
[Fact]

View File

@ -20,7 +20,7 @@ namespace BasicWebSite.Controllers
public JsonResult CustomContentType()
{
var result = new JsonResult(new { Message = "hello" });
result.ContentType = MediaTypeHeaderValue.Parse("application/message+json");
result.ContentType = "application/message+json";
return result;
}

View File

@ -38,9 +38,11 @@ namespace BasicWebSite.Formatters
builder.AppendLine();
builder.AppendLine("END:VCARD");
var selectedEncoding = MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8;
await context.HttpContext.Response.WriteAsync(
builder.ToString(),
context.ContentType?.Encoding ?? Encoding.UTF8);
selectedEncoding);
}
}
}

View File

@ -41,9 +41,11 @@ namespace BasicWebSite.Formatters
builder.AppendLine();
builder.AppendLine("END:VCARD");
var selectedEncoding = MediaTypeEncoding.GetEncoding(context.ContentType) ?? Encoding.UTF8;
await context.HttpContext.Response.WriteAsync(
builder.ToString(),
context.ContentType?.Encoding ?? Encoding.UTF8);
selectedEncoding);
}
}
}

View File

@ -14,7 +14,7 @@ namespace FiltersWebSite
context.Result = new ContentResult()
{
Content = "4",
ContentType = new MediaTypeHeaderValue("text/plain")
ContentType = "text/plain"
};
}

View File

@ -14,7 +14,7 @@ namespace FiltersWebSite
context.Result = new ContentResult
{
Content = "The Action was never executed",
ContentType = new MediaTypeHeaderValue("text/plain")
ContentType = "text/plain"
};
}
}

View File

@ -21,7 +21,7 @@ namespace FiltersWebSite
return new ContentResult()
{
Content = content,
ContentType = new MediaTypeHeaderValue("text/plain"),
ContentType = "text/plain",
};
}
}