Added PushWriter and PopWriter to RazorPageBase

- Removed WriteXYZTo methods
This commit is contained in:
Ajay Bhargav Baaskaran 2017-05-08 16:40:19 -07:00
parent 42a4e9a143
commit 5eabae55cb
3 changed files with 142 additions and 153 deletions

View File

@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// </summary>
public abstract class RazorPageBase : IRazorPage
{
private readonly Stack<TextWriter> _textWriterStack = new Stack<TextWriter>();
private StringWriter _valueBuffer;
private ITagHelperFactory _tagHelperFactory;
private IViewBufferScope _bufferScope;
@ -275,6 +276,25 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return content;
}
// Internal for unit testing.
protected internal virtual void PushWriter(TextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_textWriterStack.Push(ViewContext.Writer);
ViewContext.Writer = writer;
}
// Internal for unit testing.
protected internal virtual TextWriter PopWriter()
{
ViewContext.Writer = _textWriterStack.Pop();
return ViewContext.Writer;
}
public virtual string Href(string contentPath)
{
if (contentPath == null)
@ -335,63 +355,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// <param name="value">The <see cref="object"/> to write.</param>
public virtual void Write(object value)
{
WriteTo(Output, value);
}
/// <summary>
/// Writes the specified <paramref name="value"/> with HTML encoding to <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="object"/> to write.</param>
/// <remarks>
/// <paramref name="value"/>s of type <see cref="IHtmlContent"/> are written using
/// <see cref="IHtmlContent.WriteTo(TextWriter, HtmlEncoder)"/>.
/// For all other types, the encoded result of <see cref="object.ToString"/> is written to the
/// <paramref name="writer"/>.
/// </remarks>
public virtual void WriteTo(TextWriter writer, object value)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
WriteTo(writer, HtmlEncoder, value);
}
/// <summary>
/// Writes the specified <paramref name="value"/> with HTML encoding to given <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="encoder">
/// The <see cref="System.Text.Encodings.Web.HtmlEncoder"/> to use when encoding <paramref name="value"/>.
/// </param>
/// <param name="value">The <see cref="object"/> to write.</param>
/// <remarks>
/// <paramref name="value"/>s of type <see cref="IHtmlContent"/> are written using
/// <see cref="IHtmlContent.WriteTo(TextWriter, HtmlEncoder)"/>.
/// For all other types, the encoded result of <see cref="object.ToString"/> is written to the
/// <paramref name="writer"/>.
/// </remarks>
public static void WriteTo(TextWriter writer, HtmlEncoder encoder, object value)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (encoder == null)
{
throw new ArgumentNullException(nameof(encoder));
}
if (value == null || value == HtmlString.Empty)
{
return;
}
var htmlContent = value as IHtmlContent;
if (htmlContent != null)
var writer = Output;
var encoder = HtmlEncoder;
if (value is IHtmlContent htmlContent)
{
var bufferedWriter = writer as ViewBufferTextWriter;
if (bufferedWriter == null || !bufferedWriter.IsBuffering)
@ -400,8 +371,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
}
else
{
var htmlContentContainer = value as IHtmlContentContainer;
if (htmlContentContainer != null)
if (value is IHtmlContentContainer htmlContentContainer)
{
// This is likely another ViewBuffer.
htmlContentContainer.MoveTo(bufferedWriter.Buffer);
@ -417,26 +387,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return;
}
WriteTo(writer, encoder, value.ToString());
Write(value.ToString());
}
/// <summary>
/// Writes the specified <paramref name="value"/> with HTML encoding to <paramref name="writer"/>.
/// Writes the specified <paramref name="value"/> with HTML encoding to <see cref="Output"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="string"/> to write.</param>
public virtual void WriteTo(TextWriter writer, string value)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
WriteTo(writer, HtmlEncoder, value);
}
private static void WriteTo(TextWriter writer, HtmlEncoder encoder, string value)
public virtual void Write(string value)
{
var writer = Output;
var encoder = HtmlEncoder;
if (!string.IsNullOrEmpty(value))
{
// Perf: Encode right away instead of writing it character-by-character.
@ -452,42 +413,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// <param name="value">The <see cref="object"/> to write.</param>
public virtual void WriteLiteral(object value)
{
WriteLiteralTo(Output, value);
}
/// <summary>
/// Writes the specified <paramref name="value"/> without HTML encoding to the <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="object"/> to write.</param>
public virtual void WriteLiteralTo(TextWriter writer, object value)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (value != null)
{
WriteLiteralTo(writer, value.ToString());
}
WriteLiteral(value.ToString());
}
/// <summary>
/// Writes the specified <paramref name="value"/> without HTML encoding to <see cref="Output"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="string"/> to write.</param>
public virtual void WriteLiteralTo(TextWriter writer, string value)
public virtual void WriteLiteral(string value)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (!string.IsNullOrEmpty(value))
{
writer.Write(value);
Output.Write(value);
}
}
@ -499,23 +436,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
int suffixOffset,
int attributeValuesCount)
{
BeginWriteAttributeTo(Output, name, prefix, prefixOffset, suffix, suffixOffset, attributeValuesCount);
}
public virtual void BeginWriteAttributeTo(
TextWriter writer,
string name,
string prefix,
int prefixOffset,
string suffix,
int suffixOffset,
int attributeValuesCount)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (prefix == null)
{
throw new ArgumentNullException(nameof(prefix));
@ -532,7 +452,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
// null or false. Consequently defer the prefix generation until we encounter the attribute value.
if (attributeValuesCount != 1)
{
WritePositionTaggedLiteral(writer, prefix, prefixOffset);
WritePositionTaggedLiteral(prefix, prefixOffset);
}
}
@ -543,18 +463,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
int valueOffset,
int valueLength,
bool isLiteral)
{
WriteAttributeValueTo(Output, prefix, prefixOffset, value, valueOffset, valueLength, isLiteral);
}
public void WriteAttributeValueTo(
TextWriter writer,
string prefix,
int prefixOffset,
object value,
int valueOffset,
int valueLength,
bool isLiteral)
{
if (_attributeInfo.AttributeValuesCount == 1)
{
@ -566,7 +474,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
}
// We are not omitting the attribute. Write the prefix.
WritePositionTaggedLiteral(writer, _attributeInfo.Prefix, _attributeInfo.PrefixOffset);
WritePositionTaggedLiteral(_attributeInfo.Prefix, _attributeInfo.PrefixOffset);
if (IsBoolTrueWithEmptyPrefixValue(prefix, value))
{
@ -582,12 +490,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
if (!string.IsNullOrEmpty(prefix))
{
WritePositionTaggedLiteral(writer, prefix, prefixOffset);
WritePositionTaggedLiteral(prefix, prefixOffset);
}
BeginContext(valueOffset, valueLength, isLiteral);
WriteUnprefixedAttributeValueTo(writer, value, isLiteral);
WriteUnprefixedAttributeValue(value, isLiteral);
EndContext();
}
@ -595,19 +503,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor
public virtual void EndWriteAttribute()
{
EndWriteAttributeTo(Output);
}
public virtual void EndWriteAttributeTo(TextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (!_attributeInfo.Suppressed)
{
WritePositionTaggedLiteral(writer, _attributeInfo.Suffix, _attributeInfo.SuffixOffset);
WritePositionTaggedLiteral(_attributeInfo.Suffix, _attributeInfo.SuffixOffset);
}
}
@ -668,12 +566,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
_valueBuffer = new StringWriter();
}
PushWriter(_valueBuffer);
if (!string.IsNullOrEmpty(prefix))
{
WriteLiteralTo(_valueBuffer, prefix);
WriteLiteral(prefix);
}
WriteUnprefixedAttributeValueTo(_valueBuffer, value, isLiteral);
WriteUnprefixedAttributeValue(value, isLiteral);
PopWriter();
}
}
@ -738,33 +638,33 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return HtmlString.Empty;
}
private void WriteUnprefixedAttributeValueTo(TextWriter writer, object value, bool isLiteral)
private void WriteUnprefixedAttributeValue(object value, bool isLiteral)
{
var stringValue = value as string;
// The extra branching here is to ensure that we call the Write*To(string) overload where possible.
if (isLiteral && stringValue != null)
{
WriteLiteralTo(writer, stringValue);
WriteLiteral(stringValue);
}
else if (isLiteral)
{
WriteLiteralTo(writer, value);
WriteLiteral(value);
}
else if (stringValue != null)
{
WriteTo(writer, stringValue);
Write(stringValue);
}
else
{
WriteTo(writer, value);
Write(value);
}
}
private void WritePositionTaggedLiteral(TextWriter writer, string value, int position)
private void WritePositionTaggedLiteral(string value, int position)
{
BeginContext(position, value.Length, isLiteral: true);
WriteLiteralTo(writer, value);
WriteLiteral(value);
EndContext();
}

View File

@ -1394,7 +1394,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
[Theory]
[MemberData(nameof(WriteAttributeData))]
public void WriteAttributeTo_WritesAsExpected(
public void WriteAttribute_UsesSpecifiedWriter_WritesAsExpected(
Tuple<string, int, object, int, bool>[] attributeValues,
string expectedOutput)
{
@ -1406,11 +1406,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor
var suffix = string.Empty;
// Act
page.BeginWriteAttributeTo(writer, "someattr", prefix, 0, suffix, 0, attributeValues.Length);
page.PushWriter(writer);
page.BeginWriteAttribute("someattr", prefix, 0, suffix, 0, attributeValues.Length);
foreach (var value in attributeValues)
{
page.WriteAttributeValueTo(
writer,
page.WriteAttributeValue(
value.Item1,
value.Item2,
value.Item3,
@ -1418,12 +1418,89 @@ namespace Microsoft.AspNetCore.Mvc.Razor
value.Item3?.ToString().Length ?? 0,
value.Item5);
}
page.EndWriteAttributeTo(writer);
page.EndWriteAttribute();
page.PopWriter();
// Assert
Assert.Equal(expectedOutput, writer.ToString());
}
[Fact]
public void PushWriter_SetsUnderlyingWriter()
{
// Arrange
var page = CreatePage(p => { });
var writer = new StringWriter();
// Act
page.PushWriter(writer);
// Assert
Assert.Same(writer, page.ViewContext.Writer);
}
[Fact]
public void PopWriter_ResetsUnderlyingWriter()
{
// Arrange
var page = CreatePage(p => { });
var defaultWriter = new StringWriter();
page.ViewContext.Writer = defaultWriter;
var writer = new StringWriter();
// Act 1
page.PushWriter(writer);
// Assert 1
Assert.Same(writer, page.ViewContext.Writer);
// Act 2
var poppedWriter = page.PopWriter();
// Assert 2
Assert.Same(defaultWriter, poppedWriter);
Assert.Same(defaultWriter, page.ViewContext.Writer);
}
[Fact]
public void WriteLiteral_BuffersResultToPushedWriter()
{
// Arrange
var page = CreatePage(p => { });
var defaultWriter = new StringWriter();
page.ViewContext.Writer = defaultWriter;
var bufferWriter = new StringWriter();
// Act
page.WriteLiteral("Not");
page.PushWriter(bufferWriter);
page.WriteLiteral("This should be buffered");
page.PopWriter();
page.WriteLiteral(" buffered");
// Assert
Assert.Equal("Not buffered", defaultWriter.ToString());
Assert.Equal("This should be buffered", bufferWriter.ToString());
}
[Fact]
public void Write_StringValue_UsesSpecifiedWriter_EncodesValue()
{
// Arrange
var page = CreatePage(p => { });
var bufferWriter = new StringWriter();
// Act
page.PushWriter(bufferWriter);
page.Write("This should be encoded");
page.PopWriter();
// Assert
Assert.Equal("HtmlEncode[[This should be encoded]]", bufferWriter.ToString());
}
[Fact]
public async Task Write_WithHtmlString_WritesValueWithoutEncoding()
{

View File

@ -13,10 +13,22 @@ namespace RazorWebSite
base.WriteLiteral(value);
}
public override void WriteLiteral(string value)
{
base.WriteLiteral("WriteLiteral says:");
base.WriteLiteral(value);
}
public override void Write(object value)
{
base.WriteLiteral("Write says:");
base.Write(value);
}
public override void Write(string value)
{
base.WriteLiteral("Write says:");
base.Write(value);
}
}
}