Treat `ContentType==null` and `MediaType.GetEncoding(ContentType)==null` differently
- now, if client sends e.g. `Content-type: text/json;charset=[invalid]`, service will respond with HTTP/415
- if client sends `Content-type: text/json`, service will still try UTF-8 (or updated default encoding)
- intentional (and slight) behaviour change from 1ac7315
- include and a few new tests and some cleanup from same commit
This commit is contained in:
parent
116c927e61
commit
f04286562e
|
|
@ -64,7 +64,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <param name="context">The <see cref="InputFormatterContext"/>.</param>
|
/// <param name="context">The <see cref="InputFormatterContext"/>.</param>
|
||||||
/// <param name="encoding">The <see cref="Encoding"/> used to read the request body.</param>
|
/// <param name="encoding">The <see cref="Encoding"/> used to read the request body.</param>
|
||||||
/// <returns>A <see cref="Task"/> that on completion deserializes the request body.</returns>
|
/// <returns>A <see cref="Task"/> that on completion deserializes the request body.</returns>
|
||||||
public abstract Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding);
|
public abstract Task<InputFormatterResult> ReadRequestBodyAsync(
|
||||||
|
InputFormatterContext context,
|
||||||
|
Encoding encoding);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an <see cref="Encoding"/> based on <paramref name="context"/>'s
|
/// Returns an <see cref="Encoding"/> based on <paramref name="context"/>'s
|
||||||
|
|
@ -82,25 +84,31 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SupportedEncodings?.Count == 0)
|
if (SupportedEncodings.Count == 0)
|
||||||
{
|
{
|
||||||
var message = Resources.FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(nameof(SupportedEncodings));
|
var message = Resources.FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(
|
||||||
|
nameof(SupportedEncodings));
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = context.HttpContext.Request;
|
var requestContentType = context.HttpContext.Request.ContentType;
|
||||||
|
var requestMediaType = requestContentType == null ? default(MediaType) : new MediaType(requestContentType);
|
||||||
var requestEncoding = request.ContentType == null ? null : MediaType.GetEncoding(request.ContentType);
|
if (requestMediaType.Charset.HasValue)
|
||||||
if (requestEncoding != null)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < SupportedEncodings.Count; i++)
|
// Create Encoding based on requestMediaType.Charset to support charset aliases and custom Encoding
|
||||||
|
// providers. Charset -> Encoding -> encoding.WebName chain canonicalizes the charset name.
|
||||||
|
var requestEncoding = requestMediaType.Encoding;
|
||||||
|
if (requestEncoding != null)
|
||||||
{
|
{
|
||||||
if (string.Equals(
|
for (int i = 0; i < SupportedEncodings.Count; i++)
|
||||||
requestEncoding.WebName,
|
|
||||||
SupportedEncodings[i].WebName,
|
|
||||||
StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return SupportedEncodings[i];
|
if (string.Equals(
|
||||||
|
requestEncoding.WebName,
|
||||||
|
SupportedEncodings[i].WebName,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return SupportedEncodings[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +124,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
// cases where the client doesn't send a content type header or sends a content
|
// cases where the client doesn't send a content type header or sends a content
|
||||||
// type header without encoding. For that reason we pick the first encoding of the
|
// type header without encoding. For that reason we pick the first encoding of the
|
||||||
// list of supported encodings and try to use that to read the body. This encoding
|
// list of supported encodings and try to use that to read the body. This encoding
|
||||||
// is UTF-8 by default on our formatters, which generally is a safe choice for the
|
// is UTF-8 by default in our formatters, which generally is a safe choice for the
|
||||||
// encoding.
|
// encoding.
|
||||||
return SupportedEncodings[0];
|
return SupportedEncodings[0];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
Assert.Throws<InvalidOperationException>(() => formatter.TestSelectCharacterEncoding(context));
|
Assert.Throws<InvalidOperationException>(() => formatter.TestSelectCharacterEncoding(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void SelectCharacterEncoding_ReturnsNull_IfItCanNotUnderstandContentTypeEncoding()
|
[InlineData("utf-8")]
|
||||||
|
[InlineData("invalid")]
|
||||||
|
public void SelectCharacterEncoding_ReturnsNull_IfItCanNotUnderstandContentTypeEncoding(string charset)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var formatter = new TestFormatter();
|
var formatter = new TestFormatter();
|
||||||
|
|
@ -76,7 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
|
new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
|
||||||
(stream, encoding) => new StreamReader(stream, encoding));
|
(stream, encoding) => new StreamReader(stream, encoding));
|
||||||
|
|
||||||
context.HttpContext.Request.ContentType = "application/json;charset=utf-8";
|
context.HttpContext.Request.ContentType = "application/json;charset=" + charset;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = formatter.TestSelectCharacterEncoding(context);
|
var result = formatter.TestSelectCharacterEncoding(context);
|
||||||
|
|
@ -110,9 +112,79 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("application/json")]
|
[InlineData("unicode-1-1-utf-8")]
|
||||||
|
[InlineData("unicode-2-0-utf-8")]
|
||||||
|
[InlineData("unicode-1-1-utf-8")]
|
||||||
|
[InlineData("unicode-2-0-utf-8")]
|
||||||
|
public void SelectCharacterEncoding_ReturnsUTF8Encoding_IfContentTypeIsAnAlias(string charset)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestFormatter();
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.UTF32);
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.UTF8);
|
||||||
|
|
||||||
|
var context = new InputFormatterContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
"something",
|
||||||
|
new ModelStateDictionary(),
|
||||||
|
new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
|
||||||
|
(stream, encoding) => new StreamReader(stream, encoding));
|
||||||
|
|
||||||
|
context.HttpContext.Request.ContentType = "application/json;charset=" + charset;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = formatter.TestSelectCharacterEncoding(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(Encoding.UTF8, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("ANSI_X3.4-1968")]
|
||||||
|
[InlineData("ANSI_X3.4-1986")]
|
||||||
|
[InlineData("ascii")]
|
||||||
|
[InlineData("cp367")]
|
||||||
|
[InlineData("csASCII")]
|
||||||
|
[InlineData("IBM367")]
|
||||||
|
[InlineData("iso-ir-6")]
|
||||||
|
[InlineData("ISO646-US")]
|
||||||
|
[InlineData("ISO_646.irv:1991")]
|
||||||
|
[InlineData("us")]
|
||||||
|
public void SelectCharacterEncoding_ReturnsAsciiEncoding_IfContentTypeIsAnAlias(string charset)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formatter = new TestFormatter();
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.UTF32);
|
||||||
|
formatter.SupportedEncodings.Add(Encoding.ASCII);
|
||||||
|
|
||||||
|
var context = new InputFormatterContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
"something",
|
||||||
|
new ModelStateDictionary(),
|
||||||
|
new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
|
||||||
|
(stream, encoding) => new StreamReader(stream, encoding));
|
||||||
|
|
||||||
|
context.HttpContext.Request.ContentType = "application/json;charset=\"" + charset + "\"";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = formatter.TestSelectCharacterEncoding(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(Encoding.ASCII, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
[InlineData("")]
|
[InlineData("")]
|
||||||
public void SelectCharacterEncoding_ReturnsFirstEncoding_IfContentTypeIsNotSpecifiedOrDoesNotHaveEncoding(string contentType)
|
[InlineData("(garbage)")]
|
||||||
|
[InlineData("(garbage); charset=utf-32")]
|
||||||
|
[InlineData("text/(garbage)")]
|
||||||
|
[InlineData("text/(garbage); charset=utf-32")]
|
||||||
|
[InlineData("application/json")]
|
||||||
|
[InlineData("application/json; charset")]
|
||||||
|
[InlineData("application/json; charset=(garbage)")]
|
||||||
|
[InlineData("application/json; version=(garbage); charset=utf-32")]
|
||||||
|
public void SelectCharacterEncoding_ReturnsFirstEncoding_IfContentTypeIsMissingInvalidOrDoesNotHaveEncoding(
|
||||||
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var formatter = new TestFormatter();
|
var formatter = new TestFormatter();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue