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");
|
||||
}
|
||||
|
||||
[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()
|
||||
{
|
||||
return CreateUser();
|
||||
|
|
|
|||
|
|
@ -18,14 +18,17 @@ namespace Microsoft.AspNet.Mvc
|
|||
private readonly Stream _innerStream;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new object of DelegatingStream
|
||||
/// Initializes a new <see cref="DelegatingStream"/>.
|
||||
/// </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)
|
||||
{
|
||||
_innerStream = innerStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The inner stream this object delegates to.
|
||||
/// </summary>
|
||||
protected Stream InnerStream
|
||||
{
|
||||
get { return _innerStream; }
|
||||
|
|
@ -122,7 +125,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// <inheritdoc />
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
// Do nothing, we want to explicitly avoid flush because it turns on Chunked encoding.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -46,11 +46,6 @@ namespace Microsoft.AspNet.Mvc
|
|||
{
|
||||
var jsonSerializer = CreateJsonSerializer();
|
||||
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 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// <returns>The type of the object to be serialized.</returns>
|
||||
public virtual Type GetObjectType([NotNull] OutputFormatterContext context)
|
||||
{
|
||||
if (context.DeclaredType == null)
|
||||
if (context.DeclaredType == null ||
|
||||
context.DeclaredType == typeof(object))
|
||||
{
|
||||
if (context.Object != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,11 +61,13 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// <inheritdoc />
|
||||
public override Task WriteResponseBodyAsync([NotNull] OutputFormatterContext context)
|
||||
{
|
||||
var response = context.ActionContext.HttpContext.Response;
|
||||
|
||||
var tempWriterSettings = WriterSettings.Clone();
|
||||
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));
|
||||
dataContractSerializer.WriteObject(xmlWriter, context.Object);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
var tempWriterSettings = WriterSettings.Clone();
|
||||
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));
|
||||
xmlSerializer.Serialize(xmlWriter, context.Object);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#if NET45
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
|
|
@ -36,6 +40,52 @@ namespace Microsoft.AspNet.Mvc
|
|||
// Assert
|
||||
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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
[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]
|
||||
public void XmlDataContractSerializer_CanWriteResult_ReturnsTrue_ForWritableType()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -199,6 +200,21 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
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,
|
||||
string contentType = "application/xml; charset=utf-8")
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue