Write our ViewBuffer values asynchronously

This commit is contained in:
Ryan Nowak 2016-01-08 14:37:11 -08:00
parent 57bf12311b
commit 2eb6cd655b
5 changed files with 133 additions and 31 deletions

View File

@ -8,6 +8,7 @@ using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Html;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Mvc.ViewFeatures.Buffer;
namespace Microsoft.AspNet.Mvc.Razor
{
@ -24,11 +25,12 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary>
/// <param name="unbufferedWriter">The <see cref="TextWriter"/> to write output to when this instance
/// is no longer buffering.</param>
/// <param name="buffer">The <see cref="IHtmlContentBuilder"/> to buffer output to.</param>
/// <param name="buffer">The <see cref="ViewBuffer"/> to buffer output to.</param>
/// <param name="encoder">The HTML encoder.</param>
public RazorTextWriter(TextWriter unbufferedWriter, IHtmlContentBuilder buffer, HtmlEncoder encoder)
public RazorTextWriter(TextWriter unbufferedWriter, ViewBuffer buffer, HtmlEncoder encoder)
{
UnbufferedWriter = unbufferedWriter;
Buffer = buffer;
HtmlEncoder = encoder;
BufferedWriter = new HtmlContentWrapperTextWriter(buffer, unbufferedWriter.Encoding);
@ -47,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <summary>
/// Gets the buffered content.
/// </summary>
public IHtmlContent Buffer => BufferedWriter.ContentBuilder;
public ViewBuffer Buffer { get; }
// Internal for unit testing
internal HtmlContentWrapperTextWriter BufferedWriter { get; }

View File

@ -233,7 +233,7 @@ namespace Microsoft.AspNet.Mvc.Razor
if (bodyWriter.IsBuffering)
{
// Only copy buffered content to the Output if we're currently buffering.
bodyWriter.Buffer.WriteTo(context.Writer, _htmlEncoder);
await bodyWriter.Buffer.WriteToAsync(context.Writer, _htmlEncoder);
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Html;
using Microsoft.AspNet.Mvc.Rendering;
@ -142,7 +143,73 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Buffer
for (var j = 0; j < count; j++)
{
var value = segment[j];
value.WriteTo(writer, encoder);
var valueAsString = value.Value as string;
if (valueAsString != null)
{
writer.Write(valueAsString);
continue;
}
var valueAsHtmlContent = value.Value as IHtmlContent;
if (valueAsHtmlContent != null)
{
valueAsHtmlContent.WriteTo(writer, encoder);
continue;
}
}
}
}
/// <summary>
/// Writes the buffered content to <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/>.</param>
/// <param name="encoder">The <see cref="HtmlEncoder"/>.</param>
/// <returns>A <see cref="Task"/> which will complete once content has been written.</returns>
public async Task WriteToAsync(TextWriter writer, HtmlEncoder encoder)
{
if (BufferSegments == null)
{
return;
}
var htmlTextWriter = writer as HtmlTextWriter;
if (htmlTextWriter != null)
{
htmlTextWriter.Write(this);
return;
}
for (var i = 0; i < BufferSegments.Count; i++)
{
var segment = BufferSegments[i];
var count = i == BufferSegments.Count - 1 ? CurrentCount : segment.Length;
for (var j = 0; j < count; j++)
{
var value = segment[j];
var valueAsString = value.Value as string;
if (valueAsString != null)
{
await writer.WriteAsync(valueAsString);
continue;
}
var valueAsViewBuffer = value.Value as ViewBuffer;
if (valueAsViewBuffer != null)
{
await valueAsViewBuffer.WriteToAsync(writer, encoder);
continue;
}
var valueAsHtmlContent = value.Value as IHtmlContent;
if (valueAsHtmlContent != null)
{
valueAsHtmlContent.WriteTo(writer, encoder);
continue;
}
}
}
}

View File

@ -35,31 +35,5 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures.Buffer
/// Gets the value.
/// </summary>
public object Value { get; }
/// <summary>
/// Writes the <see cref="Value"/> by encoding it with the specified <paramref name="encoder"/> to the
/// specified <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> to write the value to.</param>
/// <param name="encoder">The <see cref="HtmlEncoder"/> which encodes the content to be written.</param>
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
if (Value == null)
{
return;
}
var stringValue = Value as string;
if (stringValue != null)
{
writer.Write(stringValue);
}
else
{
Debug.Assert(Value is IHtmlContent);
var htmlContentValue = (IHtmlContent)Value;
htmlContentValue.WriteTo(writer, encoder);
}
}
}
}

View File

@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Html;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewFeatures.Buffer;
@ -166,5 +167,63 @@ namespace Microsoft.AspNet.Mvc.Razor.Buffer
// Assert
Assert.Equal(expected, writer.ToString());
}
[Fact]
public async Task WriteToAsync_WritesSelf_WhenWriterIsHtmlTextWriter()
{
// Arrange
var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name");
var htmlWriter = new Mock<HtmlTextWriter>();
htmlWriter.Setup(w => w.Write(buffer)).Verifiable();
// Act
buffer.Append("Hello world");
await buffer.WriteToAsync(htmlWriter.Object, new HtmlTestEncoder());
// Assert
htmlWriter.Verify();
}
[Fact]
public async Task WriteToAsync_WritesRazorValues_ToTextWriter()
{
// Arrange
var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name");
var writer = new StringWriter();
// Act
buffer.Append("Hello");
buffer.AppendHtml(new HtmlString(" world"));
buffer.AppendHtml(" 123");
await buffer.WriteToAsync(writer, new HtmlTestEncoder());
// Assert
Assert.Equal("Hello world 123", writer.ToString());
}
[Theory]
[InlineData(9)]
[InlineData(10)]
[InlineData(11)]
[InlineData(23)]
public async Task WriteToAsync_WritesRazorValuesFromAllBuffers(int valuesToWrite)
{
// Arrange
var buffer = new ViewBuffer(new TestViewBufferScope(4), "some-name");
var writer = new StringWriter();
var expected = string.Join("", Enumerable.Range(0, valuesToWrite).Select(_ => "abc"));
// Act
for (var i = 0; i < valuesToWrite; i++)
{
buffer.AppendHtml("abc");
}
await buffer.WriteToAsync(writer, new HtmlTestEncoder());
// Assert
Assert.Equal(expected, writer.ToString());
}
}
}