Prevent flushing when writing out from the formatters.
Also make the XML formatter respect object as a return type. Issues: MVC/1073, MVC/972, MVC/958 1072 is a followup work item to add test coverage.
This commit is contained in:
parent
ddea73b934
commit
e40dbcaebf
|
|
@ -97,7 +97,7 @@ namespace MvcSample.Web
|
||||||
Context.Response.WriteAsync("Hello World raw");
|
Context.Response.WriteAsync("Hello World raw");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Produces("application/json", "application/custom", "text/json", Type = typeof(User))]
|
[Produces("application/json", "application/xml", "application/custom", "text/json", Type = typeof(User))]
|
||||||
public object ReturnUser()
|
public object ReturnUser()
|
||||||
{
|
{
|
||||||
return CreateUser();
|
return CreateUser();
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,17 @@ namespace Microsoft.AspNet.Mvc
|
||||||
private readonly Stream _innerStream;
|
private readonly Stream _innerStream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new object of DelegatingStream
|
/// Initializes a new <see cref="DelegatingStream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="innerStream">The stream on which should not be closed</param>
|
/// <param name="innerStream">The stream which should not be closed or flushed.</param>
|
||||||
public DelegatingStream([NotNull] Stream innerStream)
|
public DelegatingStream([NotNull] Stream innerStream)
|
||||||
{
|
{
|
||||||
_innerStream = innerStream;
|
_innerStream = innerStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The inner stream this object delegates to.
|
||||||
|
/// </summary>
|
||||||
protected Stream InnerStream
|
protected Stream InnerStream
|
||||||
{
|
{
|
||||||
get { return _innerStream; }
|
get { return _innerStream; }
|
||||||
|
|
@ -122,7 +125,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Flush()
|
public override void Flush()
|
||||||
{
|
{
|
||||||
_innerStream.Flush();
|
// Do nothing, we want to explicitly avoid flush because it turns on Chunked encoding.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
var jsonSerializer = CreateJsonSerializer();
|
var jsonSerializer = CreateJsonSerializer();
|
||||||
jsonSerializer.Serialize(jsonWriter, value);
|
jsonSerializer.Serialize(jsonWriter, value);
|
||||||
|
|
||||||
// We're explicitly calling flush here to simplify the debugging experience because the
|
|
||||||
// underlying TextWriter might be long-lived. If this method ends up being called repeatedly
|
|
||||||
// for a request, we should revisit.
|
|
||||||
jsonWriter.Flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,7 +72,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
var response = context.ActionContext.HttpContext.Response;
|
var response = context.ActionContext.HttpContext.Response;
|
||||||
var selectedEncoding = context.SelectedEncoding;
|
var selectedEncoding = context.SelectedEncoding;
|
||||||
using (var writer = new StreamWriter(response.Body, selectedEncoding, 1024, leaveOpen: true))
|
|
||||||
|
using (var delegatingStream = new DelegatingStream(response.Body))
|
||||||
|
using (var writer = new StreamWriter(delegatingStream, selectedEncoding, 1024, leaveOpen: true))
|
||||||
{
|
{
|
||||||
WriteObject(writer, context.Object);
|
WriteObject(writer, context.Object);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// <returns>The type of the object to be serialized.</returns>
|
/// <returns>The type of the object to be serialized.</returns>
|
||||||
public virtual Type GetObjectType([NotNull] OutputFormatterContext context)
|
public virtual Type GetObjectType([NotNull] OutputFormatterContext context)
|
||||||
{
|
{
|
||||||
if (context.DeclaredType == null)
|
if (context.DeclaredType == null ||
|
||||||
|
context.DeclaredType == typeof(object))
|
||||||
{
|
{
|
||||||
if (context.Object != null)
|
if (context.Object != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
|
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
|
||||||
{
|
{
|
||||||
var response = context.ActionContext.HttpContext.Response;
|
|
||||||
|
|
||||||
var tempWriterSettings = WriterSettings.Clone();
|
var tempWriterSettings = WriterSettings.Clone();
|
||||||
tempWriterSettings.Encoding = context.SelectedEncoding;
|
tempWriterSettings.Encoding = context.SelectedEncoding;
|
||||||
using (var xmlWriter = CreateXmlWriter(response.Body, tempWriterSettings))
|
|
||||||
|
var innerStream = context.ActionContext.HttpContext.Response.Body;
|
||||||
|
|
||||||
|
using (var outputStream = new DelegatingStream(innerStream))
|
||||||
|
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
|
||||||
{
|
{
|
||||||
var dataContractSerializer = (DataContractSerializer)CreateSerializer(GetObjectType(context));
|
var dataContractSerializer = (DataContractSerializer)CreateSerializer(GetObjectType(context));
|
||||||
dataContractSerializer.WriteObject(xmlWriter, context.Object);
|
dataContractSerializer.WriteObject(xmlWriter, context.Object);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
var tempWriterSettings = WriterSettings.Clone();
|
var tempWriterSettings = WriterSettings.Clone();
|
||||||
tempWriterSettings.Encoding = context.SelectedEncoding;
|
tempWriterSettings.Encoding = context.SelectedEncoding;
|
||||||
using (var xmlWriter = CreateXmlWriter(response.Body, tempWriterSettings))
|
|
||||||
|
var innerStream = context.ActionContext.HttpContext.Response.Body;
|
||||||
|
|
||||||
|
using (var outputStream = new DelegatingStream(innerStream))
|
||||||
|
using (var xmlWriter = CreateXmlWriter(outputStream, tempWriterSettings))
|
||||||
{
|
{
|
||||||
var xmlSerializer = (XmlSerializer)CreateSerializer(GetObjectType(context));
|
var xmlSerializer = (XmlSerializer)CreateSerializer(GetObjectType(context));
|
||||||
xmlSerializer.Serialize(xmlWriter, context.Object);
|
xmlSerializer.Serialize(xmlWriter, context.Object);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
|
|
||||||
#if NET45
|
#if NET45
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -36,6 +40,52 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(innerStream.CanRead);
|
Assert.True(innerStream.CanRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InnerStreamIsNotFlushedOnDispose()
|
||||||
|
{
|
||||||
|
var stream = FlushReportingStream.GetThrowingStream();
|
||||||
|
var delegatingStream = new DelegatingStream(stream);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
delegatingStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InnerStreamIsNotFlushedOnClose()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var stream = FlushReportingStream.GetThrowingStream();
|
||||||
|
|
||||||
|
var delegatingStream = new DelegatingStream(stream);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
delegatingStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InnerStreamIsNotFlushedOnFlush()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var stream = FlushReportingStream.GetThrowingStream();
|
||||||
|
|
||||||
|
var delegatingStream = new DelegatingStream(stream);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
delegatingStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InnerStreamIsNotFlushedOnFlushAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var stream = FlushReportingStream.GetThrowingStream();
|
||||||
|
|
||||||
|
var delegatingStream = new DelegatingStream(stream);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await delegatingStream.FlushAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Core
|
||||||
|
{
|
||||||
|
public static class FlushReportingStream
|
||||||
|
{
|
||||||
|
public static Stream GetThrowingStream()
|
||||||
|
{
|
||||||
|
var mock = new Mock<Stream>();
|
||||||
|
mock.Verify(m => m.Flush(), Times.Never());
|
||||||
|
mock.Verify(m => m.FlushAsync(It.IsAny<CancellationToken>()), Times.Never());
|
||||||
|
|
||||||
|
return mock.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -182,6 +182,21 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
|
Assert.True(outputFormatterContext.ActionContext.HttpContext.Response.Body.CanRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task XmlSerializerOutputFormatterDoesntFlushOutputStream()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var sampleInput = new DummyClass { SampleInt = 10 };
|
||||||
|
var formatter = new XmlDataContractSerializerOutputFormatter();
|
||||||
|
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
|
||||||
|
|
||||||
|
var response = outputFormatterContext.ActionContext.HttpContext.Response;
|
||||||
|
response.Body = FlushReportingStream.GetThrowingStream();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await formatter.WriteAsync(outputFormatterContext);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void XmlDataContractSerializer_CanWriteResult_ReturnsTrue_ForWritableType()
|
public void XmlDataContractSerializer_CanWriteResult_ReturnsTrue_ForWritableType()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -199,6 +200,21 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
Assert.True(formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")));
|
Assert.True(formatter.CanWriteResult(outputFormatterContext, MediaTypeHeaderValue.Parse("application/xml")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task XmlSerializerOutputFormatterDoesntFlushOutputStream()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var sampleInput = new DummyClass { SampleInt = 10 };
|
||||||
|
var formatter = new XmlSerializerOutputFormatter();
|
||||||
|
var outputFormatterContext = GetOutputFormatterContext(sampleInput, sampleInput.GetType());
|
||||||
|
|
||||||
|
var response = outputFormatterContext.ActionContext.HttpContext.Response;
|
||||||
|
response.Body = FlushReportingStream.GetThrowingStream();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await formatter.WriteAsync(outputFormatterContext);
|
||||||
|
}
|
||||||
|
|
||||||
private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType,
|
private OutputFormatterContext GetOutputFormatterContext(object outputValue, Type outputType,
|
||||||
string contentType = "application/xml; charset=utf-8")
|
string contentType = "application/xml; charset=utf-8")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue