Merge pull request #8537 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Pranav K 2018-10-02 14:25:28 -07:00 committed by GitHub
commit 60325c8010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 25 deletions

View File

@ -15,12 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
bool MethodMatches()
{
var methodNameMatchBehavior = GetNameMatchBehavior(conventionMethod);
if (!IsNameMatch(methodInfo.Name, conventionMethod.Name, methodNameMatchBehavior))
{
return false;
}
return true;
return IsNameMatch(methodInfo.Name, conventionMethod.Name, methodNameMatchBehavior);
}
bool ParametersMatch()

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@ -84,21 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
throw new ArgumentNullException(nameof(result));
}
// If the user sets the content type both on the ObjectResult (example: by Produces) and Response object,
// then the one set on ObjectResult takes precedence over the Response object
if (result.ContentTypes == null || result.ContentTypes.Count == 0)
{
var responseContentType = context.HttpContext.Response.ContentType;
if (!string.IsNullOrEmpty(responseContentType))
{
if (result.ContentTypes == null)
{
result.ContentTypes = new MediaTypeCollection();
}
result.ContentTypes.Add(responseContentType);
}
}
InferContentTypes(context, result);
var objectType = result.DeclaredType;
if (objectType == null || objectType == typeof(object))
@ -113,8 +100,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
result.Value);
var selectedFormatter = FormatterSelector.SelectFormatter(
formatterContext,
(IList<IOutputFormatter>)result.Formatters ?? Array.Empty<IOutputFormatter>(),
formatterContext,
(IList<IOutputFormatter>)result.Formatters ?? Array.Empty<IOutputFormatter>(),
result.ContentTypes);
if (selectedFormatter == null)
{
@ -130,5 +117,27 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
result.OnFormatting(context);
return selectedFormatter.WriteAsync(formatterContext);
}
private static void InferContentTypes(ActionContext context, ObjectResult result)
{
Debug.Assert(result.ContentTypes != null);
if (result.ContentTypes.Count != 0)
{
return;
}
// If the user sets the content type both on the ObjectResult (example: by Produces) and Response object,
// then the one set on ObjectResult takes precedence over the Response object
var responseContentType = context.HttpContext.Response.ContentType;
if (!string.IsNullOrEmpty(responseContentType))
{
result.ContentTypes.Add(responseContentType);
}
else if (result.Value is ProblemDetails)
{
result.ContentTypes.Add("application/problem+json");
result.ContentTypes.Add("application/problem+xml");
}
}
}
}

View File

@ -11,11 +11,13 @@ namespace Microsoft.AspNetCore.Mvc
{
public class ObjectResult : ActionResult, IStatusCodeActionResult
{
private MediaTypeCollection _contentTypes;
public ObjectResult(object value)
{
Value = value;
Formatters = new FormatterCollection<IOutputFormatter>();
ContentTypes = new MediaTypeCollection();
_contentTypes = new MediaTypeCollection();
}
[ActionResultObjectValue]
@ -23,7 +25,11 @@ namespace Microsoft.AspNetCore.Mvc
public FormatterCollection<IOutputFormatter> Formatters { get; set; }
public MediaTypeCollection ContentTypes { get; set; }
public MediaTypeCollection ContentTypes
{
get => _contentTypes;
set => _contentTypes = value ?? throw new ArgumentNullException(nameof(value));
}
public Type DeclaredType { get; set; }

View File

@ -17,6 +17,32 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
public class ObjectResultExecutorTest
{
[Fact]
public async Task ExecuteAsync_UsesSpecifiedContentType()
{
// Arrange
var executor = CreateExecutor();
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext() { HttpContext = httpContext };
httpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used
httpContext.Response.ContentType = "text/json";
var result = new ObjectResult("input")
{
ContentTypes = { "text/xml", },
};
result.Formatters.Add(new TestXmlOutputFormatter());
result.Formatters.Add(new TestJsonOutputFormatter());
result.Formatters.Add(new TestStringOutputFormatter()); // This will be chosen based on the content type
// Act
await executor.ExecuteAsync(actionContext, result);
// Assert
MediaTypeAssert.Equal("text/xml; charset=utf-8", httpContext.Response.ContentType);
}
// For this test case probably the most common use case is when there is a format mapping based
// content type selected but the developer had set the content type on the Response.ContentType
[Fact]
@ -85,6 +111,74 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
Assert.Equal(406, httpContext.Response.StatusCode);
}
[Fact]
public async Task ExecuteAsync_ForProblemDetailsValue_UsesSpecifiedContentType()
{
// Arrange
var executor = CreateExecutor();
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext() { HttpContext = httpContext };
httpContext.Response.ContentType = "application/json";
var result = new ObjectResult(new ProblemDetails())
{
ContentTypes = { "text/plain" },
};
result.Formatters.Add(new TestXmlOutputFormatter());
result.Formatters.Add(new TestJsonOutputFormatter());
result.Formatters.Add(new TestStringOutputFormatter()); // This will be chosen based on the content type
// Act
await executor.ExecuteAsync(actionContext, result);
// Assert
MediaTypeAssert.Equal("text/plain; charset=utf-8", httpContext.Response.ContentType);
}
[Fact]
public async Task ExecuteAsync_ForProblemDetailsValue_UsesResponseContentType()
{
// Arrange
var executor = CreateExecutor();
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext() { HttpContext = httpContext };
httpContext.Response.ContentType = "application/json";
var result = new ObjectResult(new ProblemDetails());
result.Formatters.Add(new TestXmlOutputFormatter());
result.Formatters.Add(new TestJsonOutputFormatter()); // This will be chosen based on the response content type
result.Formatters.Add(new TestStringOutputFormatter());
// Act
await executor.ExecuteAsync(actionContext, result);
// Assert
MediaTypeAssert.Equal("application/json; charset=utf-8", httpContext.Response.ContentType);
}
[Fact]
public async Task ExecuteAsync_NoContentTypeProvidedForProblemDetails_UsesDefaultContentTypes()
{
// Arrange
var executor = CreateExecutor();
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext() { HttpContext = httpContext };
var result = new ObjectResult(new ProblemDetails());
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the implicitly added content type
result.Formatters.Add(new TestJsonOutputFormatter());
result.Formatters.Add(new TestStringOutputFormatter());
// Act
await executor.ExecuteAsync(actionContext, result);
// Assert
MediaTypeAssert.Equal("application/problem+xml; charset=utf-8", httpContext.Response.ContentType);
}
[Fact]
public async Task ExecuteAsync_NoFormatterFound_Returns406()
{
@ -310,6 +404,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/*+json"));
SupportedEncodings.Add(Encoding.UTF8);
}
@ -326,6 +421,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/*+xml"));
SupportedEncodings.Add(Encoding.UTF8);
}