Support caching of the Content-Type string used by OutputFormatter
when the encoding is Utf-8.
This commit is contained in:
parent
830fd410f5
commit
739f83a978
|
|
@ -69,8 +69,13 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
public static string ReplaceEncoding(StringSegment mediaType, Encoding encoding)
|
public static string ReplaceEncoding(StringSegment mediaType, Encoding encoding)
|
||||||
{
|
{
|
||||||
var parsedMediaType = MediaTypeHeaderValue.Parse(mediaType.Value);
|
var parsedMediaType = MediaTypeHeaderValue.Parse(mediaType.Value);
|
||||||
parsedMediaType.Encoding = encoding;
|
|
||||||
|
|
||||||
|
if (string.Equals(parsedMediaType.Encoding?.WebName, encoding?.WebName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return mediaType.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedMediaType.Encoding = encoding;
|
||||||
return parsedMediaType.ToString();
|
return parsedMediaType.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
||||||
{
|
{
|
||||||
|
private IDictionary<string, string> _outputMediaTypeCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -40,6 +42,28 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MediaTypeCollection SupportedMediaTypes { get; }
|
public MediaTypeCollection SupportedMediaTypes { get; }
|
||||||
|
|
||||||
|
private IDictionary<string, string> OutputMediaTypeCache
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_outputMediaTypeCache == null)
|
||||||
|
{
|
||||||
|
var cache = new Dictionary<string, string>();
|
||||||
|
foreach (var mediaType in SupportedMediaTypes)
|
||||||
|
{
|
||||||
|
cache.Add(mediaType, MediaTypeEncoding.ReplaceEncoding(mediaType, Encoding.UTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe race condition, worst case scenario we initialize the field multiple times with dictionaries containing
|
||||||
|
// the same values.
|
||||||
|
_outputMediaTypeCache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _outputMediaTypeCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating whether or not the given type can be written by this serializer.
|
/// Returns a value indicating whether or not the given type can be written by this serializer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -244,31 +268,15 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
/// <returns>A task which can write the response body.</returns>
|
/// <returns>A task which can write the response body.</returns>
|
||||||
public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context);
|
public abstract Task WriteResponseBodyAsync(OutputFormatterWriteContext context);
|
||||||
|
|
||||||
/// <summary>
|
private string GetMediaTypeWithCharset(string mediaType, Encoding encoding)
|
||||||
/// 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 (string.Equals(encoding.WebName, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
if (mediaTypeEncoding == encoding)
|
OutputMediaTypeCache.ContainsKey(mediaType))
|
||||||
{
|
{
|
||||||
return mediaType;
|
return OutputMediaTypeCache[mediaType];
|
||||||
}
|
|
||||||
else if (mediaTypeEncoding == null)
|
|
||||||
{
|
|
||||||
return CreateMediaTypeWithEncoding(mediaType, encoding);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This can happen if the user has overriden SelectCharacterEncoding
|
|
||||||
return MediaTypeEncoding.ReplaceEncoding(mediaType, encoding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return MediaTypeEncoding.ReplaceEncoding(mediaType, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Encoding MatchAcceptCharacterEncoding(IList<StringWithQualityHeaderValue> acceptCharsetHeaders)
|
private Encoding MatchAcceptCharacterEncoding(IList<StringWithQualityHeaderValue> acceptCharsetHeaders)
|
||||||
|
|
@ -354,15 +362,5 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
sorted.Reverse();
|
sorted.Reverse();
|
||||||
return sorted;
|
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}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.TestCommon;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -101,6 +102,88 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
Assert.Equal(new StringSegment(expectedContentType), formatterContext.ContentType);
|
Assert.Equal(new StringSegment(expectedContentType), formatterContext.ContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WriteResponse_GetMediaTypeWithCharsetReturnsMediaTypeFromCache_IfEncodingIsUtf8()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestOutputFormatter();
|
||||||
|
|
||||||
|
var formatterContext = new OutputFormatterWriteContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||||
|
objectType: null,
|
||||||
|
@object: null)
|
||||||
|
{
|
||||||
|
ContentType = new StringSegment("application/json"),
|
||||||
|
};
|
||||||
|
|
||||||
|
formatter.SupportedMediaTypes.Add("application/json");
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.UTF8);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
formatter.WriteAsync(formatterContext);
|
||||||
|
var firstContentType = formatterContext.ContentType;
|
||||||
|
|
||||||
|
formatterContext.ContentType = new StringSegment("application/json");
|
||||||
|
|
||||||
|
formatter.WriteAsync(formatterContext);
|
||||||
|
var secondContentType = formatterContext.ContentType;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(firstContentType.Buffer, secondContentType.Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WriteResponse_GetMediaTypeWithCharsetReplacesCharset_IfDifferentThanEncoding()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestOutputFormatter();
|
||||||
|
|
||||||
|
var formatterContext = new OutputFormatterWriteContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||||
|
objectType: null,
|
||||||
|
@object: null)
|
||||||
|
{
|
||||||
|
ContentType = new StringSegment("application/json; charset=utf-7"),
|
||||||
|
};
|
||||||
|
|
||||||
|
formatter.SupportedMediaTypes.Add("application/json");
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.UTF8);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
formatter.WriteAsync(formatterContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(new StringSegment("application/json; charset=utf-8"), formatterContext.ContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WriteResponse_GetMediaTypeWithCharsetReturnsSameString_IfCharsetEqualToEncoding()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestOutputFormatter();
|
||||||
|
|
||||||
|
var contentType = "application/json; charset=utf-16";
|
||||||
|
var formatterContext = new OutputFormatterWriteContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new TestHttpResponseStreamWriterFactory().CreateWriter,
|
||||||
|
objectType: null,
|
||||||
|
@object: null)
|
||||||
|
{
|
||||||
|
ContentType = new StringSegment(contentType),
|
||||||
|
};
|
||||||
|
|
||||||
|
formatter.SupportedMediaTypes.Add("application/json");
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.Unicode);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
formatter.WriteAsync(formatterContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(contentType, formatterContext.ContentType.Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WriteResponseContentHeaders_NoSupportedEncodings_NoEncodingIsSet()
|
public void WriteResponseContentHeaders_NoSupportedEncodings_NoEncodingIsSet()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue