Use buffer pooling in IOutputFormatters

This commit is contained in:
Ryan Nowak 2015-10-20 09:14:38 -07:00
parent 9658060019
commit bcde82cf62
27 changed files with 270 additions and 76 deletions

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
using System.Text;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc.Formatters namespace Microsoft.AspNet.Mvc.Formatters
@ -15,16 +17,23 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// Creates a new <see cref="OutputFormatterWriteContext"/>. /// Creates a new <see cref="OutputFormatterWriteContext"/>.
/// </summary> /// </summary>
/// <param name="httpContext">The <see cref="Http.HttpContext"/> for the current request.</param> /// <param name="httpContext">The <see cref="Http.HttpContext"/> for the current request.</param>
/// <param name="writerFactory">The delegate used to create a <see cref="TextWriter"/> for writing the response.</param>
/// <param name="objectType">The <see cref="Type"/> of the object to write to the response.</param> /// <param name="objectType">The <see cref="Type"/> of the object to write to the response.</param>
/// <param name="@object">The object to write to the response.</param> /// <param name="object">The object to write to the response.</param>
public OutputFormatterWriteContext(HttpContext httpContext, Type objectType, object @object) public OutputFormatterWriteContext(HttpContext httpContext, Func<Stream, Encoding, TextWriter> writerFactory, Type objectType, object @object)
{ {
if (httpContext == null) if (httpContext == null)
{ {
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (writerFactory == null)
{
throw new ArgumentNullException(nameof(writerFactory));
}
HttpContext = httpContext; HttpContext = httpContext;
WriterFactory = writerFactory;
ObjectType = objectType; ObjectType = objectType;
Object = @object; Object = @object;
} }
@ -33,5 +42,10 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// Gets or sets the <see cref="HttpContext"/> context associated with the current operation. /// Gets or sets the <see cref="HttpContext"/> context associated with the current operation.
/// </summary> /// </summary>
public virtual HttpContext HttpContext { get; protected set; } public virtual HttpContext HttpContext { get; protected set; }
/// <summary>
/// Gets or sets a delegate used to create a <see cref="TextWriter"/> for writing the response.
/// </summary>
public virtual Func<Stream, Encoding, TextWriter> WriterFactory { get; protected set; }
} }
} }

View File

@ -3,7 +3,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
@ -32,6 +34,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
public ObjectResultExecutor( public ObjectResultExecutor(
IOptions<MvcOptions> options, IOptions<MvcOptions> options,
IActionBindingContextAccessor bindingContextAccessor, IActionBindingContextAccessor bindingContextAccessor,
IHttpResponseStreamWriterFactory writerFactory,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
if (options == null) if (options == null)
@ -54,6 +57,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
OptionsFormatters = options.Value.OutputFormatters; OptionsFormatters = options.Value.OutputFormatters;
RespectBrowserAcceptHeader = options.Value.RespectBrowserAcceptHeader; RespectBrowserAcceptHeader = options.Value.RespectBrowserAcceptHeader;
Logger = loggerFactory.CreateLogger<ObjectResultExecutor>(); Logger = loggerFactory.CreateLogger<ObjectResultExecutor>();
WriterFactory = writerFactory.CreateWriter;
} }
/// <summary> /// <summary>
@ -76,6 +80,11 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
/// </summary> /// </summary>
protected bool RespectBrowserAcceptHeader { get; } protected bool RespectBrowserAcceptHeader { get; }
/// <summary>
/// Gets the writer factory delegate.
/// </summary>
protected Func<Stream, Encoding, TextWriter> WriterFactory { get; }
/// <summary> /// <summary>
/// Executes the <see cref="ObjectResult"/>. /// Executes the <see cref="ObjectResult"/>.
/// </summary> /// </summary>
@ -110,7 +119,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
objectType = result.Value?.GetType(); objectType = result.Value?.GetType();
}; };
var formatterContext = new OutputFormatterWriteContext(context.HttpContext, objectType, result.Value); var formatterContext = new OutputFormatterWriteContext(
context.HttpContext,
WriterFactory,
objectType,
result.Value);
var selectedFormatter = SelectFormatter(formatterContext, result.ContentTypes, formatters); var selectedFormatter = SelectFormatter(formatterContext, result.ContentTypes, formatters);
if (selectedFormatter == null) if (selectedFormatter == null)
{ {

View File

@ -108,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
var selectedEncoding = context.ContentType?.Encoding ?? Encoding.UTF8; var selectedEncoding = context.ContentType?.Encoding ?? Encoding.UTF8;
using (var writer = new HttpResponseStreamWriter(response.Body, selectedEncoding)) using (var writer = context.WriterFactory(response.Body, selectedEncoding))
{ {
WriteObject(writer, context.Object); WriteObject(writer, context.Object);
} }

View File

@ -148,17 +148,23 @@ namespace Microsoft.AspNet.Mvc.Formatters
} }
/// <summary> /// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given stream and the <see cref="WriterSettings"/>. /// Creates a new instance of <see cref="XmlWriter"/> using the given <see cref="TextWriter"/> and
/// <see cref="XmlWriterSettings"/>.
/// </summary> /// </summary>
/// <param name="writeStream">The stream on which the XmlWriter should operate on.</param> /// <param name="writer">
/// The underlying <see cref="TextWriter"/> which the <see cref="XmlWriter"/> should write to.
/// </param>
/// <param name="xmlWriterSettings">
/// The <see cref="XmlWriterSettings"/>.
/// </param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns> /// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter( public virtual XmlWriter CreateXmlWriter(
Stream writeStream, TextWriter writer,
XmlWriterSettings xmlWriterSettings) XmlWriterSettings xmlWriterSettings)
{ {
if (writeStream == null) if (writer == null)
{ {
throw new ArgumentNullException(nameof(writeStream)); throw new ArgumentNullException(nameof(writer));
} }
if (xmlWriterSettings == null) if (xmlWriterSettings == null)
@ -166,9 +172,10 @@ namespace Microsoft.AspNet.Mvc.Formatters
throw new ArgumentNullException(nameof(xmlWriterSettings)); throw new ArgumentNullException(nameof(xmlWriterSettings));
} }
return XmlWriter.Create( // We always close the TextWriter, so the XmlWriter shouldn't.
new HttpResponseStreamWriter(writeStream, xmlWriterSettings.Encoding), xmlWriterSettings.CloseOutput = false;
xmlWriterSettings);
return XmlWriter.Create(writer, xmlWriterSettings);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -182,24 +189,26 @@ namespace Microsoft.AspNet.Mvc.Formatters
var writerSettings = WriterSettings.Clone(); var writerSettings = WriterSettings.Clone();
writerSettings.Encoding = context.ContentType?.Encoding ?? Encoding.UTF8; writerSettings.Encoding = context.ContentType?.Encoding ?? Encoding.UTF8;
// Wrap the object only if there is a wrapping type.
var value = context.Object; var value = context.Object;
var wrappingType = GetSerializableType(context.ObjectType);
using (var xmlWriter = CreateXmlWriter(context.HttpContext.Response.Body, writerSettings)) if (wrappingType != null && wrappingType != context.ObjectType)
{ {
var wrappingType = GetSerializableType(context.ObjectType); var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(new WrapperProviderContext(
declaredType: context.ObjectType,
isSerialization: true));
// Wrap the object only if there is a wrapping type. value = wrapperProvider.Wrap(value);
if (wrappingType != null && wrappingType != context.ObjectType) }
var dataContractSerializer = GetCachedSerializer(wrappingType);
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
{
using (var xmlWriter = CreateXmlWriter(textWriter, writerSettings))
{ {
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(new WrapperProviderContext( dataContractSerializer.WriteObject(xmlWriter, value);
declaredType: context.ObjectType,
isSerialization: true));
value = wrapperProvider.Wrap(value);
} }
var dataContractSerializer = GetCachedSerializer(wrappingType);
dataContractSerializer.WriteObject(xmlWriter, value);
} }
return TaskCache.CompletedTask; return TaskCache.CompletedTask;

View File

@ -123,17 +123,23 @@ namespace Microsoft.AspNet.Mvc.Formatters
} }
/// <summary> /// <summary>
/// Creates a new instance of <see cref="XmlWriter"/> using the given stream and the <see cref="WriterSettings"/>. /// Creates a new instance of <see cref="XmlWriter"/> using the given <see cref="TextWriter"/> and
/// <see cref="XmlWriterSettings"/>.
/// </summary> /// </summary>
/// <param name="writeStream">The stream on which the XmlWriter should operate on.</param> /// <param name="writer">
/// The underlying <see cref="TextWriter"/> which the <see cref="XmlWriter"/> should write to.
/// </param>
/// <param name="xmlWriterSettings">
/// The <see cref="XmlWriterSettings"/>.
/// </param>
/// <returns>A new instance of <see cref="XmlWriter"/></returns> /// <returns>A new instance of <see cref="XmlWriter"/></returns>
public virtual XmlWriter CreateXmlWriter( public virtual XmlWriter CreateXmlWriter(
Stream writeStream, TextWriter writer,
XmlWriterSettings xmlWriterSettings) XmlWriterSettings xmlWriterSettings)
{ {
if (writeStream == null) if (writer == null)
{ {
throw new ArgumentNullException(nameof(writeStream)); throw new ArgumentNullException(nameof(writer));
} }
if (xmlWriterSettings == null) if (xmlWriterSettings == null)
@ -141,9 +147,10 @@ namespace Microsoft.AspNet.Mvc.Formatters
throw new ArgumentNullException(nameof(xmlWriterSettings)); throw new ArgumentNullException(nameof(xmlWriterSettings));
} }
return XmlWriter.Create( // We always close the TextWriter, so the XmlWriter shouldn't.
new HttpResponseStreamWriter(writeStream, xmlWriterSettings.Encoding), xmlWriterSettings.CloseOutput = false;
xmlWriterSettings);
return XmlWriter.Create(writer, xmlWriterSettings);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -159,24 +166,26 @@ namespace Microsoft.AspNet.Mvc.Formatters
var writerSettings = WriterSettings.Clone(); var writerSettings = WriterSettings.Clone();
writerSettings.Encoding = context.ContentType.Encoding ?? Encoding.UTF8; writerSettings.Encoding = context.ContentType.Encoding ?? Encoding.UTF8;
// Wrap the object only if there is a wrapping type.
var value = context.Object; var value = context.Object;
var wrappingType = GetSerializableType(context.ObjectType);
using (var xmlWriter = CreateXmlWriter(context.HttpContext.Response.Body, writerSettings)) if (wrappingType != null && wrappingType != context.ObjectType)
{ {
var wrappingType = GetSerializableType(context.ObjectType); var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(new WrapperProviderContext(
declaredType: context.ObjectType,
isSerialization: true));
// Wrap the object only if there is a wrapping type. value = wrapperProvider.Wrap(value);
if (wrappingType != null && wrappingType != context.ObjectType) }
var xmlSerializer = GetCachedSerializer(wrappingType);
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
{
using (var xmlWriter = CreateXmlWriter(textWriter, writerSettings))
{ {
var wrapperProvider = WrapperProviderFactories.GetWrapperProvider(new WrapperProviderContext( xmlSerializer.Serialize(xmlWriter, value);
declaredType: context.ObjectType,
isSerialization: true));
value = wrapperProvider.Wrap(value);
} }
var xmlSerializer = GetCachedSerializer(wrappingType);
xmlSerializer.Serialize(xmlWriter, value);
} }
return TaskCache.CompletedTask; return TaskCache.CompletedTask;

View File

@ -98,6 +98,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -112,6 +112,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -99,6 +99,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -18,7 +18,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Arrange // Arrange
var formatter = new HttpNotAcceptableOutputFormatter(); var formatter = new HttpNotAcceptableOutputFormatter();
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null)
{ {
FailedContentNegotiation = failedContentNegotiation, FailedContentNegotiation = failedContentNegotiation,
}; };
@ -36,7 +40,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Arrange // Arrange
var formatter = new HttpNotAcceptableOutputFormatter(); var formatter = new HttpNotAcceptableOutputFormatter();
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null)
{ {
FailedContentNegotiation = true, FailedContentNegotiation = true,
}; };
@ -54,7 +62,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Arrange // Arrange
var formatter = new HttpNotAcceptableOutputFormatter(); var formatter = new HttpNotAcceptableOutputFormatter();
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
// Act // Act
await formatter.WriteAsync(context); await formatter.WriteAsync(context);

View File

@ -43,7 +43,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var type = declaredTypeAsString ? typeof(string) : typeof(object); var type = declaredTypeAsString ? typeof(string) : typeof(object);
var contentType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null; var contentType = useNonNullContentType ? MediaTypeHeaderValue.Parse("text/plain") : null;
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), type, value) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
value)
{ {
ContentType = contentType, ContentType = contentType,
}; };
@ -65,6 +69,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Arrange // Arrange
var context = new OutputFormatterWriteContext( var context = new OutputFormatterWriteContext(
new DefaultHttpContext(), new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
declaredType, declaredType,
"Something non null.") "Something non null.")
{ {
@ -92,6 +97,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
// Arrange // Arrange
var context = new OutputFormatterWriteContext( var context = new OutputFormatterWriteContext(
new DefaultHttpContext(), new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string), typeof(string),
value) value)
{ {
@ -114,7 +120,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
public async Task WriteAsync_WritesTheStatusCode204() public async Task WriteAsync_WritesTheStatusCode204()
{ {
// Arrange // Arrange
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), typeof(string), @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string),
@object: null);
var formatter = new HttpNoContentOutputFormatter(); var formatter = new HttpNoContentOutputFormatter();
@ -132,7 +142,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Response.StatusCode = StatusCodes.Status201Created; httpContext.Response.StatusCode = StatusCodes.Status201Created;
var context = new OutputFormatterWriteContext(httpContext, typeof(string), @object: null); var context = new OutputFormatterWriteContext(
httpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string),
@object: null);
var formatter = new HttpNoContentOutputFormatter(); var formatter = new HttpNoContentOutputFormatter();

View File

@ -53,7 +53,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding)); formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding));
} }
var context = new OutputFormatterWriteContext(httpContext.Object, typeof(string), "someValue") var context = new OutputFormatterWriteContext(
httpContext.Object,
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string),
"someValue")
{ {
ContentType = MediaTypeHeaderValue.Parse(httpRequest.Headers[HeaderNames.Accept]), ContentType = MediaTypeHeaderValue.Parse(httpRequest.Headers[HeaderNames.Accept]),
}; };
@ -77,7 +81,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedMediaTypes.Clear(); formatter.SupportedMediaTypes.Clear();
formatter.SupportedMediaTypes.Add(testContentType); formatter.SupportedMediaTypes.Add(testContentType);
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null)
{ {
ContentType = testContentType, ContentType = testContentType,
}; };
@ -102,7 +110,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var mediaType = new MediaTypeHeaderValue("image/png"); var mediaType = new MediaTypeHeaderValue("image/png");
formatter.SupportedMediaTypes.Add(mediaType); formatter.SupportedMediaTypes.Add(mediaType);
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
// Act // Act
await formatter.WriteAsync(context); await formatter.WriteAsync(context);
@ -117,7 +129,12 @@ namespace Microsoft.AspNet.Mvc.Formatters
public void CanWriteResult_ForNullContentType_UsesFirstEntryInSupportedContentTypes() public void CanWriteResult_ForNullContentType_UsesFirstEntryInSupportedContentTypes()
{ {
// Arrange // Arrange
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
var formatter = new TestOutputFormatter(); var formatter = new TestOutputFormatter();
// Act // Act
@ -151,7 +168,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json")); formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
formatter.SupportedTypes.Add(typeof(int)); formatter.SupportedTypes.Add(typeof(int));
var context = new OutputFormatterWriteContext(new DefaultHttpContext(),typeof(string), "Hello, world!") var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string),
"Hello, world!")
{ {
ContentType = formatter.SupportedMediaTypes[0], ContentType = formatter.SupportedMediaTypes[0],
}; };

View File

@ -23,7 +23,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var formatter = new StreamOutputFormatter(); var formatter = new StreamOutputFormatter();
var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType); var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType);
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), type, new MemoryStream()) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
new MemoryStream())
{ {
ContentType = contentTypeHeader, ContentType = contentTypeHeader,
}; };
@ -44,7 +48,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var formatter = new StreamOutputFormatter(); var formatter = new StreamOutputFormatter();
var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType); var contentTypeHeader = contentType == null ? null : new MediaTypeHeaderValue(contentType);
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), type, new SimplePOCO()) var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
new SimplePOCO())
{ {
ContentType = contentTypeHeader, ContentType = contentTypeHeader,
}; };
@ -66,7 +74,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var formatter = new StreamOutputFormatter(); var formatter = new StreamOutputFormatter();
var @object = type != null ? Activator.CreateInstance(type) : null; var @object = type != null ? Activator.CreateInstance(type) : null;
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), type, @object); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
@object);
// Act // Act
var result = formatter.CanWriteResult(context); var result = formatter.CanWriteResult(context);
@ -90,7 +102,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var bufferingFeature = new TestBufferingFeature(); var bufferingFeature = new TestBufferingFeature();
httpContext.Features.Set<IHttpBufferingFeature>(bufferingFeature); httpContext.Features.Set<IHttpBufferingFeature>(bufferingFeature);
var context = new OutputFormatterWriteContext(httpContext, typeof(Stream), new MemoryStream(expected)); var context = new OutputFormatterWriteContext(
httpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(Stream),
new MemoryStream(expected));
// Act // Act
await formatter.WriteAsync(context); await formatter.WriteAsync(context);

View File

@ -42,7 +42,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var formatter = new StringOutputFormatter(); var formatter = new StringOutputFormatter();
var type = useDeclaredTypeAsString ? typeof(string) : typeof(object); var type = useDeclaredTypeAsString ? typeof(string) : typeof(object);
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), type, value); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
type,
value);
context.ContentType = MediaTypeHeaderValue.Parse("application/json"); context.ContentType = MediaTypeHeaderValue.Parse("application/json");
// Act // Act
@ -66,7 +70,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
httpContext.Setup(o => o.Response).Returns(response.Object); httpContext.Setup(o => o.Response).Returns(response.Object);
var formatter = new StringOutputFormatter(); var formatter = new StringOutputFormatter();
var context = new OutputFormatterWriteContext(httpContext.Object, typeof(string), @object: null); var context = new OutputFormatterWriteContext(
httpContext.Object,
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string),
@object: null);
// Act // Act
await formatter.WriteResponseBodyAsync(context); await formatter.WriteResponseBodyAsync(context);

View File

@ -75,6 +75,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -79,6 +79,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -32,7 +32,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestJsonOutputFormatter(), // This will be chosen based on the accept header new TestJsonOutputFormatter(), // This will be chosen based on the accept header
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/json"; context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/json";
// Act // Act
@ -58,7 +63,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestJsonOutputFormatter(), // This will be chosen based on the content type new TestJsonOutputFormatter(), // This will be chosen based on the content type
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used
// Act // Act
@ -83,7 +93,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestXmlOutputFormatter(), new TestXmlOutputFormatter(),
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used context.HttpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used
// Act // Act
@ -150,7 +165,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestJsonOutputFormatter(), new TestJsonOutputFormatter(),
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = acceptHeader; context.HttpContext.Request.Headers[HeaderNames.Accept] = acceptHeader;
// Act // Act
@ -177,7 +197,11 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestXmlOutputFormatter(), new TestXmlOutputFormatter(),
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
// Act // Act
var formatter = executor.SelectFormatter( var formatter = executor.SelectFormatter(
@ -203,7 +227,12 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
new TestJsonOutputFormatter(), new TestJsonOutputFormatter(),
}; };
var context = new OutputFormatterWriteContext(new DefaultHttpContext(), objectType: null, @object: null); var context = new OutputFormatterWriteContext(
new DefaultHttpContext(),
new TestHttpResponseStreamWriterFactory().CreateWriter,
objectType: null,
@object: null);
context.HttpContext.Request.Headers[HeaderNames.Accept] = "text/custom, application/custom"; context.HttpContext.Request.Headers[HeaderNames.Accept] = "text/custom, application/custom";
// Act // Act
@ -434,6 +463,7 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
return new TestObjectResultExecutor( return new TestObjectResultExecutor(
options ?? new TestOptionsManager<MvcOptions>(), options ?? new TestOptionsManager<MvcOptions>(),
bindingContextAccessor, bindingContextAccessor,
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
@ -486,9 +516,10 @@ namespace Microsoft.AspNet.Mvc.Infrastructure
{ {
public TestObjectResultExecutor( public TestObjectResultExecutor(
IOptions<MvcOptions> options, IOptions<MvcOptions> options,
IActionBindingContextAccessor bindingContextAccessor, IActionBindingContextAccessor bindingContextAccessor,
IHttpResponseStreamWriterFactory writerFactory,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: base(options, bindingContextAccessor, loggerFactory) : base(options, bindingContextAccessor, writerFactory, loggerFactory)
{ {
} }

View File

@ -68,6 +68,7 @@ namespace Microsoft.AspNet.Mvc
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
new TestOptionsManager<MvcOptions>(), new TestOptionsManager<MvcOptions>(),
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
services.AddInstance<ILoggerFactory>(NullLoggerFactory.Instance); services.AddInstance<ILoggerFactory>(NullLoggerFactory.Instance);

View File

@ -175,6 +175,7 @@ namespace Microsoft.AspNet.Mvc.Formatters
var outputFormatterContext = new OutputFormatterWriteContext( var outputFormatterContext = new OutputFormatterWriteContext(
actionContext.HttpContext, actionContext.HttpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
typeof(string), typeof(string),
content) content)
{ {
@ -218,7 +219,11 @@ namespace Microsoft.AspNet.Mvc.Formatters
var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType); var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType);
var actionContext = GetActionContext(mediaTypeHeaderValue, responseStream); var actionContext = GetActionContext(mediaTypeHeaderValue, responseStream);
return new OutputFormatterWriteContext(actionContext.HttpContext, outputType, outputValue) return new OutputFormatterWriteContext(
actionContext.HttpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
outputType,
outputValue)
{ {
ContentType = mediaTypeHeaderValue, ContentType = mediaTypeHeaderValue,
}; };

View File

@ -599,7 +599,11 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
Type outputType, Type outputType,
string contentType = "application/xml; charset=utf-8") string contentType = "application/xml; charset=utf-8")
{ {
return new OutputFormatterWriteContext(GetHttpContext(contentType), outputType, outputValue); return new OutputFormatterWriteContext(
GetHttpContext(contentType),
new TestHttpResponseStreamWriterFactory().CreateWriter,
outputType,
outputValue);
} }
private static HttpContext GetHttpContext(string contentType) private static HttpContext GetHttpContext(string contentType)

View File

@ -360,7 +360,11 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml
Type outputType, Type outputType,
string contentType = "application/xml; charset=utf-8") string contentType = "application/xml; charset=utf-8")
{ {
return new OutputFormatterWriteContext(GetHttpContext(contentType), outputType, outputValue); return new OutputFormatterWriteContext(
GetHttpContext(contentType),
new TestHttpResponseStreamWriterFactory().CreateWriter,
outputType,
outputValue);
} }
private static HttpContext GetHttpContext(string contentType) private static HttpContext GetHttpContext(string contentType)

View File

@ -4,9 +4,11 @@
}, },
"dependencies": { "dependencies": {
"Microsoft.AspNet.Http": "1.0.0-*", "Microsoft.AspNet.Http": "1.0.0-*",
"Microsoft.AspNet.Mvc" : "6.0.0-*", "Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.Formatters.Xml" : "6.0.0-*", "Microsoft.AspNet.Mvc.Formatters.Xml" : "6.0.0-*",
"Microsoft.AspNet.Mvc.TestCommon": { "type": "build", "version": "6.0.0-*" },
"Microsoft.AspNet.Testing": "1.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*",
"Microsoft.Extensions.WebEncoders.Testing": "1.0.0-*",
"Moq": "4.2.1312.1622", "Moq": "4.2.1312.1622",
"xunit.runner.aspnet": "2.0.0-aspnet-*" "xunit.runner.aspnet": "2.0.0-aspnet-*"
}, },

View File

@ -77,6 +77,7 @@ namespace System.Web.Http
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -77,6 +77,7 @@ namespace System.Web.Http
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -151,7 +151,11 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShimTest
Type outputType, Type outputType,
HttpContext httpContext) HttpContext httpContext)
{ {
return new OutputFormatterWriteContext(httpContext, outputType, outputValue); return new OutputFormatterWriteContext(
httpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
outputType,
outputValue);
} }
} }
} }

View File

@ -90,6 +90,7 @@ namespace System.Web.Http
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -77,6 +77,7 @@ namespace System.Web.Http
services.AddInstance(new ObjectResultExecutor( services.AddInstance(new ObjectResultExecutor(
options, options,
new ActionBindingContextAccessor(), new ActionBindingContextAccessor(),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance)); NullLoggerFactory.Instance));
return services.BuildServiceProvider(); return services.BuildServiceProvider();

View File

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Text;
using Microsoft.AspNet.Mvc.Infrastructure;
namespace Microsoft.AspNet.Mvc
{
public class TestHttpResponseStreamWriterFactory : IHttpResponseStreamWriterFactory
{
public TextWriter CreateWriter(Stream stream, Encoding encoding)
{
return new HttpResponseStreamWriter(stream, encoding);
}
}
}