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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
|||
/// </summary>
|
||||
public abstract class OutputFormatter : IOutputFormatter, IApiResponseFormatMetadataProvider
|
||||
{
|
||||
private IDictionary<string, string> _outputMediaTypeCache;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -40,6 +42,28 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
|||
/// </summary>
|
||||
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>
|
||||
/// Returns a value indicating whether or not the given type can be written by this serializer.
|
||||
/// </summary>
|
||||
|
|
@ -244,31 +268,15 @@ 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)
|
||||
private string GetMediaTypeWithCharset(string mediaType, Encoding encoding)
|
||||
{
|
||||
var mediaTypeEncoding = MediaTypeEncoding.GetEncoding(mediaType);
|
||||
if (mediaTypeEncoding == encoding)
|
||||
if (string.Equals(encoding.WebName, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) &&
|
||||
OutputMediaTypeCache.ContainsKey(mediaType))
|
||||
{
|
||||
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);
|
||||
return OutputMediaTypeCache[mediaType];
|
||||
}
|
||||
|
||||
return MediaTypeEncoding.ReplaceEncoding(mediaType, encoding);
|
||||
}
|
||||
|
||||
private Encoding MatchAcceptCharacterEncoding(IList<StringWithQualityHeaderValue> acceptCharsetHeaders)
|
||||
|
|
@ -354,15 +362,5 @@ 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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.TestCommon;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Moq;
|
||||
|
|
@ -101,6 +102,88 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
|||
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]
|
||||
public void WriteResponseContentHeaders_NoSupportedEncodings_NoEncodingIsSet()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue