diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs index 844dd8fad9..f39e6ac53f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ViewResult.cs @@ -31,11 +31,23 @@ namespace Microsoft.AspNet.Mvc using (view as IDisposable) { - context.HttpContext.Response.ContentType = "text/html"; - using (var writer = new StreamWriter(context.HttpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) + context.HttpContext.Response.ContentType = "text/html; charset=utf-8"; + var wrappedStream = new StreamWrapper(context.HttpContext.Response.Body); + using (var writer = new StreamWriter(wrappedStream, new UTF8Encoding(false), 1024, leaveOpen: true)) { - var viewContext = new ViewContext(context, view, ViewData, writer); - await view.RenderAsync(viewContext); + try + { + var viewContext = new ViewContext(context, view, ViewData, writer); + await view.RenderAsync(viewContext); + } + catch + { + // Need to prevent writes/flushes on dispose because the StreamWriter will flush even if nothing + // got written. This leads to a response going out on the wire prematurely in case an exception + // is being thrown inside the try catch block. + wrappedStream.BlockWrites = true; + throw; + } } } } @@ -57,5 +69,80 @@ namespace Microsoft.AspNet.Mvc return result.View; } + + private class StreamWrapper : Stream + { + private readonly Stream _wrappedStream; + + public StreamWrapper([NotNull] Stream stream) + { + _wrappedStream = stream; + } + + public bool BlockWrites { get; set;} + + public override bool CanRead + { + get { return _wrappedStream.CanRead; } + } + + public override bool CanSeek + { + get { return _wrappedStream.CanSeek; } + } + + public override bool CanWrite + { + get { return _wrappedStream.CanWrite; } + } + + public override void Flush() + { + if (!BlockWrites) + { + _wrappedStream.Flush(); + } + } + + public override long Length + { + get { return _wrappedStream.Length; } + } + + public override long Position + { + get + { + return _wrappedStream.Position; + } + set + { + _wrappedStream.Position = value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _wrappedStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return Seek(offset, origin); + } + + public override void SetLength(long value) + { + SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (!BlockWrites) + { + _wrappedStream.Write(buffer, offset, count); + } + } + } } }