Removing CopyTo from RazorTextWriter

This commit is contained in:
Pranav K 2015-11-30 11:24:25 -08:00
parent 7e1a6222aa
commit 70bdb6eb3e
6 changed files with 33 additions and 178 deletions

View File

@ -1,10 +1,9 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc.Rendering;
namespace Microsoft.AspNet.Mvc.Razor
@ -20,9 +19,9 @@ namespace Microsoft.AspNet.Mvc.Razor
ViewContext ViewContext { get; set; }
/// <summary>
/// Gets or sets the action invoked to render the body.
/// Gets or sets the body content.
/// </summary>
Func<TextWriter, Task> RenderBodyDelegateAsync { get; set; }
IHtmlContent BodyContent { get; set; }
/// <summary>
/// Gets or sets a flag that determines if the layout of this page is being rendered.
@ -64,7 +63,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// Verifies that all sections defined in <see cref="PreviousSectionWriters"/> were rendered, or
/// the body was rendered if no sections were defined.
/// </summary>
/// <exception cref="InvalidOperationException">if one or more sections were not rendered or if no sections were
/// <exception cref="System.InvalidOperationException">if one or more sections were not rendered or if no sections were
/// defined and the body was not rendered.</exception>
void EnsureRenderedBodyOrSections();
}

View File

@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public ITempDataDictionary TempData => ViewContext?.TempData;
/// <inheritdoc />
public Func<TextWriter, Task> RenderBodyDelegateAsync { get; set; }
public IHtmlContent BodyContent { get; set; }
/// <inheritdoc />
public bool IsLayoutBeingRendered { get; set; }
@ -249,26 +249,26 @@ namespace Microsoft.AspNet.Mvc.Razor
_originalWriter = null;
}
var tagHelperContentWrapperTextWriter = new TagHelperContentWrapperTextWriter(Output.Encoding);
var tagHelperContent = new DefaultTagHelperContent();
var razorWriter = writer as RazorTextWriter;
if (razorWriter != null)
{
razorWriter.CopyTo(tagHelperContentWrapperTextWriter);
tagHelperContent.Append(razorWriter.Buffer);
}
else
{
var stringCollectionTextWriter = writer as StringCollectionTextWriter;
if (stringCollectionTextWriter != null)
{
stringCollectionTextWriter.CopyTo(tagHelperContentWrapperTextWriter, HtmlEncoder);
tagHelperContent.Append(stringCollectionTextWriter.Content);
}
else
{
tagHelperContentWrapperTextWriter.Write(writer.ToString());
tagHelperContent.AppendHtml(writer.ToString());
}
}
return tagHelperContentWrapperTextWriter.Content;
return tagHelperContent;
}
/// <summary>
@ -669,16 +669,16 @@ namespace Microsoft.AspNet.Mvc.Razor
/// In a Razor layout page, renders the portion of a content page that is not within a named section.
/// </summary>
/// <returns>The HTML content to render.</returns>
protected virtual HelperResult RenderBody()
protected virtual IHtmlContent RenderBody()
{
if (RenderBodyDelegateAsync == null)
if (BodyContent == null)
{
var message = Resources.FormatRazorPage_MethodCannotBeCalled(nameof(RenderBody), Path);
throw new InvalidOperationException(message);
}
_renderedBody = true;
return new HelperResult(RenderBodyDelegateAsync);
return BodyContent;
}
/// <summary>
@ -886,7 +886,7 @@ namespace Microsoft.AspNet.Mvc.Razor
throw new InvalidOperationException(Resources.FormatSectionsNotRendered(Path, sectionNames));
}
}
else if (RenderBodyDelegateAsync != null && !_renderedBody)
else if (BodyContent != null && !_renderedBody)
{
// There are no sections defined, but RenderBody was NOT called.
// If a body was defined, then RenderBody should have been called.

View File

@ -48,6 +48,11 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <inheritdoc />
public bool IsBuffering { get; private set; } = true;
/// <summary>
/// Gets the buffered content.
/// </summary>
public IHtmlContent Buffer => BufferedWriter.Content;
// Internal for unit testing
internal StringCollectionTextWriter BufferedWriter { get; }
@ -185,7 +190,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
IsBuffering = false;
TargetWriter = UnbufferedWriter;
CopyTo(UnbufferedWriter);
Buffer.WriteTo(UnbufferedWriter, HtmlEncoder);
}
UnbufferedWriter.Flush();
@ -197,43 +202,16 @@ namespace Microsoft.AspNet.Mvc.Razor
/// to the unbuffered writer.
/// </summary>
/// <returns>A <see cref="Task"/> that represents the asynchronous copy and flush operations.</returns>
public override async Task FlushAsync()
public override Task FlushAsync()
{
if (IsBuffering)
{
IsBuffering = false;
TargetWriter = UnbufferedWriter;
await CopyToAsync(UnbufferedWriter);
Buffer.WriteTo(UnbufferedWriter, HtmlEncoder);
}
await UnbufferedWriter.FlushAsync();
}
/// <inheritdoc />
public void CopyTo(TextWriter writer)
{
writer = UnWrapRazorTextWriter(writer);
BufferedWriter.CopyTo(writer, HtmlEncoder);
}
/// <inheritdoc />
public Task CopyToAsync(TextWriter writer)
{
writer = UnWrapRazorTextWriter(writer);
return BufferedWriter.CopyToAsync(writer, HtmlEncoder);
}
private static TextWriter UnWrapRazorTextWriter(TextWriter writer)
{
var targetRazorTextWriter = writer as RazorTextWriter;
if (targetRazorTextWriter != null)
{
writer = targetRazorTextWriter.IsBuffering ?
targetRazorTextWriter.BufferedWriter :
targetRazorTextWriter.UnbufferedWriter;
}
return writer;
return UnbufferedWriter.FlushAsync();
}
}
}

View File

@ -203,7 +203,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// in the layout.
previousPage.IsLayoutBeingRendered = true;
layoutPage.PreviousSectionWriters = previousPage.SectionWriters;
layoutPage.RenderBodyDelegateAsync = bodyWriter.CopyToAsync;
layoutPage.BodyContent = bodyWriter.Buffer;
bodyWriter = await RenderPageAsync(layoutPage, context, viewStartPages: null);
renderedLayouts.Add(layoutPage);
@ -219,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Razor
if (bodyWriter.IsBuffering)
{
// Only copy buffered content to the Output if we're currently buffering.
await bodyWriter.CopyToAsync(context.Writer);
bodyWriter.Buffer.WriteTo(context.Writer, _htmlEncoder);
}
}

View File

@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public class RazorPageTest
{
private readonly RenderAsyncDelegate _nullRenderAsyncDelegate = writer => Task.FromResult(0);
private readonly Func<TextWriter, Task> NullAsyncWrite = CreateAsyncWriteDelegate(string.Empty);
private readonly Func<TextWriter, Task> NullAsyncWrite = writer => writer.WriteAsync(string.Empty);
[Fact]
public async Task WritingScopesRedirectContentWrittenToViewContextWriter()
@ -311,7 +311,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
{ "baz", _nullRenderAsyncDelegate }
};
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("body-content");
page.BodyContent = new HtmlString("body-content");
// Act
await page.ExecuteAsync();
@ -335,7 +335,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
{ "baz", _nullRenderAsyncDelegate }
};
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("body-content");
page.BodyContent = new HtmlString("body-content");
// Act
await page.ExecuteAsync();
@ -444,7 +444,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
});
page.Path = path;
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("some content");
page.BodyContent = new HtmlString("some content");
// Act
await page.ExecuteAsync();
@ -464,7 +464,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
});
page.Path = path;
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("some content");
page.BodyContent = new HtmlString("some content");
page.PreviousSectionWriters = new Dictionary<string, RenderAsyncDelegate>
{
{ sectionName, _nullRenderAsyncDelegate }
@ -490,7 +490,7 @@ namespace Microsoft.AspNet.Mvc.Razor
v.RenderSection(sectionA);
v.RenderSection(sectionB);
});
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("some content");
page.BodyContent = new HtmlString("some content");
page.PreviousSectionWriters = new Dictionary<string, RenderAsyncDelegate>
{
{ sectionA, _nullRenderAsyncDelegate },
@ -524,7 +524,7 @@ namespace Microsoft.AspNet.Mvc.Razor
v.Write(v.RenderSection("footer"));
v.WriteLiteral("Layout end");
});
page.RenderBodyDelegateAsync = CreateAsyncWriteDelegate("body content" + Environment.NewLine);
page.BodyContent = new HtmlString("body content" + Environment.NewLine);
page.PreviousSectionWriters = new Dictionary<string, RenderAsyncDelegate>
{
{
@ -1181,11 +1181,6 @@ namespace Microsoft.AspNet.Mvc.Razor
new HtmlHelperOptions());
}
private static Func<TextWriter, Task> CreateAsyncWriteDelegate(string value)
{
return async (writer) => await writer.WriteAsync(value);
}
public abstract class TestableRazorPage : RazorPage
{
public TestableRazorPage()
@ -1202,7 +1197,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}
}
public HelperResult RenderBodyPublic()
public IHtmlContent RenderBodyPublic()
{
return base.RenderBody();
}

View File

@ -283,123 +283,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
Assert.Equal("Hello, world!", stringWriter.ToString());
}
[Fact]
public void Copy_CopiesContent_IfTargetTextWriterIsARazorTextWriterAndBuffering()
{
// Arrange
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
// Act
source.Write("Hello world");
source.Write(new char[1], 0, 1);
source.CopyTo(target);
// Assert
// Make sure content was written to the source.
Assert.Equal(2, source.BufferedWriter.Entries.Count);
Assert.Equal(1, target.BufferedWriter.Entries.Count);
Assert.Same(source.BufferedWriter.Content, Assert.Single(target.BufferedWriter.Entries));
}
[Fact]
public void Copy_CopiesContent_IfTargetTextWriterIsARazorTextWriterAndNotBuffering()
{
// Arrange
var unbufferedWriter = new Mock<TextWriter>();
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new RazorTextWriter(unbufferedWriter.Object, Encoding.UTF8, new HtmlTestEncoder());
// Act
target.Flush();
source.Write("Hello world");
source.Write(new[] { 'a', 'b', 'c', 'd' }, 1, 2);
source.CopyTo(target);
// Assert
// Make sure content was written to the source.
Assert.Equal(2, source.BufferedWriter.Entries.Count);
Assert.Empty(target.BufferedWriter.Entries);
unbufferedWriter.Verify(v => v.Write("Hello world"), Times.Once());
unbufferedWriter.Verify(v => v.Write("bc"), Times.Once());
}
[Fact]
public void Copy_WritesContent_IfTargetTextWriterIsNotARazorTextWriter()
{
// Arrange
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new StringWriter();
var expected = "Hello world" + Environment.NewLine + "abc";
// Act
source.WriteLine("Hello world");
source.Write(new[] { 'x', 'a', 'b', 'c' }, 1, 3);
source.CopyTo(target);
// Assert
Assert.Equal(expected, target.ToString());
}
[Fact]
public async Task CopyAsync_WritesContent_IfTargetTextWriterIsARazorTextWriterAndBuffering()
{
// Arrange
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
// Act
source.WriteLine("Hello world");
source.Write(new[] { 'x', 'a', 'b', 'c' }, 1, 3);
await source.CopyToAsync(target);
// Assert
Assert.Equal(3, source.BufferedWriter.Entries.Count);
Assert.Equal(1, target.BufferedWriter.Entries.Count);
Assert.Equal(source.BufferedWriter.Content, Assert.Single(target.BufferedWriter.Entries));
}
//[Fact]
// IHtmlContent currently does not support async writes. Hence disabling this test.
public async Task CopyAsync_WritesContent_IfTargetTextWriterIsARazorTextWriterAndNotBuffering()
{
// Arrange
var unbufferedWriter = new Mock<TextWriter>();
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new RazorTextWriter(unbufferedWriter.Object, Encoding.UTF8, new HtmlTestEncoder());
// Act
await target.FlushAsync();
source.WriteLine("Hello from Asp.Net");
await source.WriteAsync(new[] { 'x', 'y', 'z', 'u' }, 0, 3);
await source.CopyToAsync(target);
// Assert
// Make sure content was written to the source.
Assert.Equal(3, source.BufferedWriter.Entries.Count);
Assert.Empty(target.BufferedWriter.Content.ToString());
unbufferedWriter.Verify(v => v.WriteAsync("Hello from Asp.Net"), Times.Once());
unbufferedWriter.Verify(v => v.WriteAsync(Environment.NewLine), Times.Once());
unbufferedWriter.Verify(v => v.WriteAsync("xyz"), Times.Once());
}
[Fact]
public async Task CopyAsync_WritesContent_IfTargetTextWriterIsNotARazorTextWriter()
{
// Arrange
var source = new RazorTextWriter(TextWriter.Null, Encoding.UTF8, new HtmlTestEncoder());
var target = new StringWriter();
var expected = "Hello world" + Environment.NewLine;
// Act
source.Write("Hello ");
await source.WriteLineAsync(new[] { 'w', 'o', 'r', 'l', 'd' });
await source.CopyToAsync(target);
// Assert
Assert.Equal(expected, target.ToString());
}
private class TestClass
{
public override string ToString()