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:
Doug Bunting 2016-10-11 20:06:16 -07:00
parent 116c927e61
commit f04286562e
2 changed files with 99 additions and 19 deletions

View File

@ -64,7 +64,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// <param name="context">The <see cref="InputFormatterContext"/>.</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>
public abstract Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding);
public abstract Task<InputFormatterResult> ReadRequestBodyAsync(
InputFormatterContext context,
Encoding encoding);
/// <summary>
/// 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));
}
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);
}
var request = context.HttpContext.Request;
var requestEncoding = request.ContentType == null ? null : MediaType.GetEncoding(request.ContentType);
if (requestEncoding != null)
var requestContentType = context.HttpContext.Request.ContentType;
var requestMediaType = requestContentType == null ? default(MediaType) : new MediaType(requestContentType);
if (requestMediaType.Charset.HasValue)
{
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(
requestEncoding.WebName,
SupportedEncodings[i].WebName,
StringComparison.OrdinalIgnoreCase))
for (int i = 0; i < SupportedEncodings.Count; i++)
{
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
// 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
// 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.
return SupportedEncodings[0];
}

View File

@ -62,8 +62,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
Assert.Throws<InvalidOperationException>(() => formatter.TestSelectCharacterEncoding(context));
}
[Fact]
public void SelectCharacterEncoding_ReturnsNull_IfItCanNotUnderstandContentTypeEncoding()
[Theory]
[InlineData("utf-8")]
[InlineData("invalid")]
public void SelectCharacterEncoding_ReturnsNull_IfItCanNotUnderstandContentTypeEncoding(string charset)
{
// Arrange
var formatter = new TestFormatter();
@ -76,7 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
(stream, encoding) => new StreamReader(stream, encoding));
context.HttpContext.Request.ContentType = "application/json;charset=utf-8";
context.HttpContext.Request.ContentType = "application/json;charset=" + charset;
// Act
var result = formatter.TestSelectCharacterEncoding(context);
@ -110,9 +112,79 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
[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("")]
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
var formatter = new TestFormatter();