1. Moving the HttpNotAcceptableOutputFormatter to product code.

2. Renaming the NoContentFormatter to HttpNoContentOutputFormatter.
3. Updating the test to use mock.
This commit is contained in:
harshgMSFT 2014-09-11 16:07:34 -07:00
parent e884a476e9
commit 7a3dc352c9
8 changed files with 86 additions and 80 deletions

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Sets the status code to 204 if the content is null.
/// </summary>
public class NoContentFormatter : IOutputFormatter
public class HttpNoContentOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{

View File

@ -0,0 +1,39 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A formatter which does not have a supported content type and selects itself if no content type is passed to it.
/// Sets the status code to 406 if selected.
/// </summary>
public class HttpNotAcceptableOutputFormatter : IOutputFormatter
{
/// <inheritdoc />
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{
return contentType == null;
}
/// <inheritdoc />
public IReadOnlyList<MediaTypeHeaderValue> GetSupportedContentTypes(Type declaredType,
Type runtimeType,
MediaTypeHeaderValue contentType)
{
return null;
}
/// <inheritdoc />
public Task WriteAsync(OutputFormatterContext context)
{
var response = context.ActionContext.HttpContext.Response;
response.StatusCode = 406;
return Task.FromResult<bool>(true);
}
}
}

View File

@ -34,7 +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 HttpNoContentOutputFormatter());
options.OutputFormatters.Add(new TextPlainFormatter());
options.OutputFormatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(),
indent: false));

View File

@ -275,18 +275,23 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
}
[Theory]
[InlineData("", 2)]
[InlineData(null, 2)]
[InlineData("application/xml", 3)]
[InlineData("application/custom", 3)]
[InlineData("application/xml;q=1, application/custom;q=0.8", 4)]
[InlineData("")]
[InlineData(null)]
[InlineData("application/xml")]
[InlineData("application/custom")]
[InlineData("application/xml;q=1, application/custom;q=0.8")]
public void SelectFormatter_WithNoMatchingAcceptHeadersAndRequestContentType_PicksFormatterBasedOnObjectType
(string acceptHeader, int attemptedCountForCanWrite)
(string acceptHeader)
{
// For no accept headers,
// can write is called twice once for the request media type and once for the type match pass.
// For each additional accept header, it is called once.
// Arrange
var acceptHeaderCollection = string.IsNullOrEmpty(acceptHeader) ?
null :
acceptHeader?.Split(',')
.Select(header => MediaTypeWithQualityHeaderValue.Parse(header))
.ToArray();
var stream = new MemoryStream();
var httpResponse = new Mock<HttpResponse>();
httpResponse.SetupProperty<string>(o => o.ContentType);
@ -295,15 +300,10 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
var actionContext = CreateMockActionContext(httpResponse.Object,
requestAcceptHeader: acceptHeader,
requestContentType: "application/xml");
var requestContentType = MediaTypeHeaderValue.Parse("application/xml");
var input = "testInput";
var result = new ObjectResult(input);
// Set more than one formatters. The test output formatter throws on write.
result.Formatters = new List<IOutputFormatter>
{
new CannotWriteFormatter(),
new CountingFormatter(),
};
var mockCountingFormatter = new Mock<IOutputFormatter>();
var context = new OutputFormatterContext()
{
@ -311,13 +311,37 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
Object = input,
DeclaredType = typeof(string)
};
var mockCountingSupportedContentType = MediaTypeHeaderValue.Parse("application/text");
mockCountingFormatter.Setup(o => o.CanWriteResult(context,
It.IsNotIn<MediaTypeHeaderValue>(mockCountingSupportedContentType)))
.Returns(false);
mockCountingFormatter.Setup(o => o.CanWriteResult(context, mockCountingSupportedContentType))
.Returns(true);
mockCountingFormatter.Setup(o => o.GetSupportedContentTypes(context.DeclaredType,
input.GetType(),
It.IsAny<MediaTypeHeaderValue>()))
.Returns(new List<MediaTypeHeaderValue> { mockCountingSupportedContentType });
// Set more than one formatters. The test output formatter throws on write.
result.Formatters = new List<IOutputFormatter>
{
new CannotWriteFormatter(),
mockCountingFormatter.Object,
};
// Act
var formatter = result.SelectFormatter(context, result.Formatters);
// Assert
var countingFormatter = Assert.IsType<CountingFormatter>(formatter);
Assert.Equal(attemptedCountForCanWrite, countingFormatter.GetCanWriteCallCount());
Assert.Equal(mockCountingFormatter.Object, formatter);
mockCountingFormatter.Verify(v => v.CanWriteResult(context,
mockCountingSupportedContentType),
Times.Once());
var callCount = (acceptHeaderCollection == null ? 0 : acceptHeaderCollection.Count()) + 1;
mockCountingFormatter.Verify(v => v.CanWriteResult(context,
It.IsNotIn<MediaTypeHeaderValue>(mockCountingSupportedContentType)),
Times.Exactly(callCount));
}
[Fact]
@ -507,42 +531,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
return serviceCollection.BuildServiceProvider();
}
public class CountingFormatter : OutputFormatter
{
private int _canWriteCallsCount = 0;
public CountingFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/plain"));
SupportedEncodings.Add(Encoding.GetEncoding("utf-8"));
}
public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{
_canWriteCallsCount++;
if (base.CanWriteResult(context, contentType))
{
var actionReturnString = context.Object as string;
if (actionReturnString != null)
{
return true;
}
}
return false;
}
public int GetCanWriteCallCount()
{
return _canWriteCallsCount;
}
public override Task WriteResponseBodyAsync(OutputFormatterContext context)
{
throw new NotImplementedException();
}
}
public class CannotWriteFormatter : IOutputFormatter
{
public virtual bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)

View File

@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.Test
ActionContext = null,
};
var contetType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null;
var formatter = new NoContentFormatter();
var formatter = new HttpNoContentOutputFormatter();
// Act
var actualCanWriteResult = formatter.CanWriteResult(formatterContext, contetType);
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Test
ActionContext = new ActionContext(defaultHttpContext, new RouteData(), new ActionDescriptor())
};
var formatter = new NoContentFormatter();
var formatter = new HttpNoContentOutputFormatter();
// Act
await formatter.WriteAsync(formatterContext);

View File

@ -3,7 +3,7 @@
using System;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

View File

@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc
// Assert
Assert.Equal(5, mvcOptions.OutputFormatters.Count);
Assert.IsType<NoContentFormatter>(mvcOptions.OutputFormatters[0].Instance);
Assert.IsType<HttpNoContentOutputFormatter>(mvcOptions.OutputFormatters[0].Instance);
Assert.IsType<TextPlainFormatter>(mvcOptions.OutputFormatters[1].Instance);
Assert.IsType<JsonOutputFormatter>(mvcOptions.OutputFormatters[2].Instance);
Assert.IsType<XmlDataContractSerializerOutputFormatter>(mvcOptions.OutputFormatters[3].Instance);

View File

@ -47,7 +47,7 @@ namespace ConnegWebsite
public IActionResult OverrideTheFallback_UsingCustomFormatters(int input)
{
var objectResult = new ObjectResult(input);
objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter());
objectResult.Formatters.Add(new HttpNotAcceptableOutputFormatter());
objectResult.Formatters.Add(new PlainTextFormatter());
objectResult.Formatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), false));
return objectResult;
@ -57,7 +57,7 @@ namespace ConnegWebsite
{
var objectResult = new ObjectResult(input);
var formattersProvider = ActionContext.HttpContext.RequestServices.GetService<IOutputFormattersProvider>();
objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter());
objectResult.Formatters.Add(new HttpNotAcceptableOutputFormatter());
foreach (var formatter in formattersProvider.OutputFormatters)
{
objectResult.Formatters.Add(formatter);
@ -65,26 +65,5 @@ namespace ConnegWebsite
return objectResult;
}
public class StopIfNoMatchOutputFormatter : IOutputFormatter
{
// Select if no Registered content type.
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{
return contentType == null;
}
public IReadOnlyList<MediaTypeHeaderValue> GetSupportedContentTypes(Type declaredType, Type runtimeType, MediaTypeHeaderValue contentType)
{
return null;
}
public Task WriteAsync(OutputFormatterContext context)
{
var response = context.ActionContext.HttpContext.Response;
response.StatusCode = 406;
return Task.FromResult<bool>(true);
}
}
}
}