NoContentFormatter: Writes 204 to the response status code if the value returned is null.
This commit is contained in:
parent
538e589894
commit
1684d1d322
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the status code to 204 if the content is null.
|
||||
/// </summary>
|
||||
public class NoContentFormatter : IOutputFormatter
|
||||
{
|
||||
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||
{
|
||||
// ignore the contentType and just look at the content.
|
||||
// This formatter will be selected if the content is null.
|
||||
return context.Object == null;
|
||||
}
|
||||
|
||||
public Task WriteAsync(OutputFormatterContext context)
|
||||
{
|
||||
var response = context.ActionContext.HttpContext.Response;
|
||||
response.ContentLength = 0;
|
||||
|
||||
// Only set the status code if its not already set.
|
||||
if (response.StatusCode == 0)
|
||||
{
|
||||
response.StatusCode = 204;
|
||||
}
|
||||
|
||||
return Task.FromResult<bool>(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
<Compile Include="ActionResults\HttpNotFoundResult.cs" />
|
||||
<Compile Include="Formatters\TextPlainFormatter.cs" />
|
||||
<Compile Include="HttpMethodAttribute.cs" />
|
||||
<Compile Include="Formatters\NoContentFormatter.cs" />
|
||||
<Compile Include="Internal\DecisionTree\DecisionCriterionValue.cs" />
|
||||
<Compile Include="Internal\DecisionTree\DecisionCriterionValueEqualityComparer.cs" />
|
||||
<Compile Include="Logging\AttributeRouteRouteAsyncValues.cs" />
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
options.ModelBinders.Add(new ComplexModelDtoModelBinder());
|
||||
|
||||
// Set up default output formatters.
|
||||
options.OutputFormatters.Add(new NoContentFormatter());
|
||||
options.OutputFormatters.Add(new TextPlainFormatter());
|
||||
options.OutputFormatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(),
|
||||
indent: false));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Test
|
||||
{
|
||||
public class NoContentFormatterTests
|
||||
{
|
||||
public static IEnumerable<object[]> OutputFormatterContextValues_CanWriteType
|
||||
{
|
||||
get
|
||||
{
|
||||
// object value, bool useDeclaredTypeAsString, bool expectedCanwriteResult, bool useNonNullContentType
|
||||
yield return new object[] { "valid value", true, false, true };
|
||||
yield return new object[] { "valid value", false, false, true };
|
||||
yield return new object[] { "", false, false, true };
|
||||
yield return new object[] { "", true, false, true };
|
||||
yield return new object[] { null, true, true, true };
|
||||
yield return new object[] { null, false, true, true };
|
||||
yield return new object[] { null, false, true, false };
|
||||
yield return new object[] { new object(), false, false, true };
|
||||
yield return new object[] { 1232, false, false, true };
|
||||
yield return new object[] { 1232, false, false, false };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData("OutputFormatterContextValues_CanWriteType")]
|
||||
public void CanWriteResult_ReturnsTrueOnlyIfTheValueIsNull(object value,
|
||||
bool declaredTypeAsString,
|
||||
bool expectedCanwriteResult,
|
||||
bool useNonNullContentType)
|
||||
{
|
||||
// Arrange
|
||||
var typeToUse = declaredTypeAsString ? typeof(string) : typeof(object);
|
||||
var formatterContext = new OutputFormatterContext()
|
||||
{
|
||||
Object = value,
|
||||
DeclaredType = typeToUse,
|
||||
ActionContext = null,
|
||||
};
|
||||
var contetType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null;
|
||||
var formatter = new NoContentFormatter();
|
||||
|
||||
// Act
|
||||
var actualCanWriteResult = formatter.CanWriteResult(formatterContext, contetType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedCanwriteResult, actualCanWriteResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_WritesTheStatusCode204_IfNotAlreadySet()
|
||||
{
|
||||
// Arrange
|
||||
var defaultHttpContext = new DefaultHttpContext();
|
||||
|
||||
// Workaround for https://github.com/aspnet/HttpAbstractions/issues/114
|
||||
defaultHttpContext.Response.StatusCode = 0;
|
||||
var formatterContext = new OutputFormatterContext()
|
||||
{
|
||||
Object = null,
|
||||
ActionContext = new ActionContext(defaultHttpContext, new RouteData(), new ActionDescriptor())
|
||||
};
|
||||
|
||||
var formatter = new NoContentFormatter();
|
||||
|
||||
// Act
|
||||
await formatter.WriteAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(204, defaultHttpContext.Response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsync_DoesnNotWriteTheStatusCode204_IfStatusCodeIsSetAlready()
|
||||
{
|
||||
// Arrange
|
||||
var defaultHttpContext = new DefaultHttpContext();
|
||||
defaultHttpContext.Response.StatusCode = 201;
|
||||
var formatterContext = new OutputFormatterContext()
|
||||
{
|
||||
Object = null,
|
||||
ActionContext = new ActionContext(defaultHttpContext, new RouteData(), new ActionDescriptor())
|
||||
};
|
||||
|
||||
var formatter = new NoContentFormatter();
|
||||
|
||||
// Act
|
||||
await formatter.WriteAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(201, defaultHttpContext.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +30,11 @@
|
|||
<Compile Include="ActionResults\RedirectResultTest.cs" />
|
||||
<Compile Include="ActionResults\RedirectToActionResultTest.cs" />
|
||||
<Compile Include="ActionResults\RedirectToRouteResultTest.cs" />
|
||||
<Compile Include="ActionResults\RedirectResultTest.cs" />
|
||||
<Compile Include="DefaultActionDiscoveryConventionsActionSelectionTests.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryOptionsTests.cs" />
|
||||
<Compile Include="Filters\ProducesAttributeTests.cs" />
|
||||
<Compile Include="Formatters\NoContentFormatterTests.cs" />
|
||||
<Compile Include="Formatters\OutputFormatterTests.cs" />
|
||||
<Compile Include="Formatters\TextPlainFormatterTests.cs" />
|
||||
<Compile Include="DefaultViewComponentActivatorTests.cs" />
|
||||
|
|
@ -58,6 +61,7 @@
|
|||
<Compile Include="OptionDescriptors\ValueProviderFactoryDescriptorTest.cs" />
|
||||
<Compile Include="OptionDescriptors\ViewEngineDescriptorTest.cs" />
|
||||
<Compile Include="OptionDescriptors\ViewEngineDscriptorExtensionsTest.cs" />
|
||||
<Compile Include="MediaTypeWithQualityHeaderValueTests.cs" />
|
||||
<Compile Include="ParameterBinding\ModelBindingHelperTest.cs" />
|
||||
<Compile Include="AntiXsrf\AntiForgeryTokenSerializerTest.cs" />
|
||||
<Compile Include="AntiXsrf\ITokenProvider.cs" />
|
||||
|
|
|
|||
|
|
@ -189,65 +189,5 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
|
||||
[InlineData("ReturnTaskOfString")]
|
||||
[InlineData("ReturnTaskOfObject_StringValue")]
|
||||
[InlineData("ReturnString")]
|
||||
[InlineData("ReturnObject_StringValue")]
|
||||
[InlineData("ReturnString_NullValue")]
|
||||
public async Task TextPlainFormatter_ReturnsTextPlainContentType(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
var expectedContentType = "text/plain;charset=utf-8";
|
||||
var expectedBody = actionName;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/TextPlain/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[InlineData("ReturnTaskOfObject_ObjectValue")]
|
||||
[InlineData("ReturnObject_ObjectValue")]
|
||||
[InlineData("ReturnObject_NullValue")]
|
||||
public async Task TextPlainFormatter_DoesNotSelectTextPlainFormatterForNonStringValue(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
var expectedContentType = "application/json;charset=utf-8";
|
||||
var expectedBody = actionName;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/TextPlain/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
}
|
||||
|
||||
[InlineData("ReturnString_NullValue")]
|
||||
public async Task TextPlainFormatter_DoesNotWriteNullValue(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
var expectedContentType = "text/plain;charset=utf-8";
|
||||
string expectedBody = null;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/TextPlain/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BasicTests.cs" />
|
||||
<Compile Include="OutputFormattterTests.cs" />
|
||||
<Compile Include="ConnegTests.cs" />
|
||||
<Compile Include="AntiForgeryTests.cs" />
|
||||
<Compile Include="ModelBindingTests.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ConnegWebsite;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class OutputFormatterTests
|
||||
{
|
||||
private readonly IServiceProvider _provider = TestHelper.CreateServices("ConnegWebsite");
|
||||
private readonly Action<IBuilder> _app = new Startup().Configure;
|
||||
|
||||
[Theory]
|
||||
[InlineData("ReturnTaskOfString")]
|
||||
[InlineData("ReturnTaskOfObject_StringValue")]
|
||||
[InlineData("ReturnString")]
|
||||
[InlineData("ReturnObject_StringValue")]
|
||||
public async Task TextPlainFormatter_ForStringValues_GetsSelectedReturnsTextPlainContentType(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
var expectedContentType = "text/plain;charset=utf-8";
|
||||
var expectedBody = actionName;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/TextPlain/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ReturnTaskOfObject_ObjectValue")]
|
||||
[InlineData("ReturnObject_ObjectValue")]
|
||||
public async Task JsonOutputFormatter_ForNonStringValue_GetsSelected(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
var expectedContentType = "application/json;charset=utf-8";
|
||||
var expectedBody = actionName;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/TextPlain/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ReturnTaskOfString_NullValue")]
|
||||
[InlineData("ReturnTaskOfObject_StringValue")]
|
||||
[InlineData("ReturnTaskOfObject_NullValue")]
|
||||
[InlineData("ReturnObject_NullValue")]
|
||||
public async Task NoContentFormatter_ForNullValue_GetsSelectedAndWritesResponse(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
string expectedContentType = null;
|
||||
|
||||
// ReadBodyAsString returns empty string instead of null.
|
||||
string expectedBody = "";
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/NoContent/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expectedBody, body);
|
||||
Assert.Equal(204, result.HttpContext.Response.StatusCode);
|
||||
Assert.Equal(0, result.HttpContext.Response.ContentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
<Compile Include="Controllers\ProducesContentBaseController.cs" />
|
||||
<Compile Include="Controllers\ProducesContentOnClassController.cs" />
|
||||
<Compile Include="Controllers\HomeController.cs" />
|
||||
<Compile Include="Controllers\NoContentController.cs" />
|
||||
<Compile Include="Controllers\TextPlainController.cs" />
|
||||
<Compile Include="Models\User.cs" />
|
||||
<Compile Include="ContentType.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ConnegWebsite
|
||||
{
|
||||
public class NoContentController : Controller
|
||||
{
|
||||
public Task<string> ReturnTaskOfString_NullValue()
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task<object> ReturnTaskOfObject_NullValue()
|
||||
{
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
public string ReturnString_NullValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ReturnObject_NullValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ namespace ConnegWebsite
|
|||
|
||||
public object ReturnObject_StringValue()
|
||||
{
|
||||
return "";
|
||||
return "ReturnObject_StringValue";
|
||||
}
|
||||
|
||||
public object ReturnObject_ObjectValue()
|
||||
|
|
|
|||
Loading…
Reference in New Issue