DecorateWriter does not get called for partial views rendered via
Html.PartialAsync * Introducing StringCollectionTextWriter to buffer the contents of PartialAsync * Ensure DecorateWriter is called for partial views Fixes #1266
This commit is contained in:
parent
20eadb94ee
commit
18e11f546d
|
|
@ -6,7 +6,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a hierarchy of strings and provides an enumerator that iterates over it as a sequence.
|
||||
|
|
@ -330,11 +330,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
public async Task<HtmlString> PartialAsync([NotNull] string partialViewName, object model,
|
||||
ViewDataDictionary viewData)
|
||||
{
|
||||
using (var writer = new StringWriter(CultureInfo.CurrentCulture))
|
||||
using (var writer = new StringCollectionTextWriter(Encoding.UTF8))
|
||||
{
|
||||
await RenderPartialCoreAsync(partialViewName, model, viewData, writer);
|
||||
|
||||
return new HtmlString(writer.ToString());
|
||||
return new HtmlString(writer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public class HtmlString
|
||||
{
|
||||
private static readonly HtmlString _empty = new HtmlString(string.Empty);
|
||||
|
||||
private readonly StringCollectionTextWriter _writer;
|
||||
private readonly string _input;
|
||||
|
||||
public HtmlString(string input)
|
||||
|
|
@ -14,6 +17,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
_input = input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="HtmlString"/> that is backed by <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
public HtmlString([NotNull] StringCollectionTextWriter writer)
|
||||
{
|
||||
_writer = writer;
|
||||
}
|
||||
|
||||
public static HtmlString Empty
|
||||
{
|
||||
get
|
||||
|
|
@ -22,8 +34,30 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the value in this instance of <see cref="HtmlString"/> to the target <paramref name="targetWriter"/>.
|
||||
/// </summary>
|
||||
/// <param name="targetWriter">The <see cref="TextWriter"/> to write contents to.</param>
|
||||
public void WriteTo(TextWriter targetWriter)
|
||||
{
|
||||
if (_writer != null)
|
||||
{
|
||||
_writer.CopyTo(targetWriter);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetWriter.Write(_input);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (_writer != null)
|
||||
{
|
||||
return _writer.ToString();
|
||||
}
|
||||
|
||||
return _input;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TextWriter"/> that represents individual write operations as a sequence of strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is primarily designed to avoid creating large in-memory strings.
|
||||
/// Refer to https://aspnetwebstack.codeplex.com/workitem/585 for more details.
|
||||
/// </remarks>
|
||||
public class StringCollectionTextWriter : TextWriter
|
||||
{
|
||||
private static readonly Task _completedTask = Task.FromResult(0);
|
||||
private readonly Encoding _encoding;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="StringCollectionTextWriter"/>.
|
||||
/// </summary>
|
||||
/// <param name="encoding">The character <see cref="Encoding"/> in which the output is written.</param>
|
||||
public StringCollectionTextWriter(Encoding encoding)
|
||||
{
|
||||
_encoding = encoding;
|
||||
Buffer = new BufferEntryCollection();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Encoding Encoding
|
||||
{
|
||||
get { return _encoding; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection of entries buffered by this instance of <see cref="StringCollectionTextWriter"/>.
|
||||
/// </summary>
|
||||
public BufferEntryCollection Buffer { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(char value)
|
||||
{
|
||||
Buffer.Add(value.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write([NotNull] char[] buffer, int index, int count)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
if (count < 0 || (buffer.Length - index < count))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
Buffer.Add(buffer, index, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Buffer.Add(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync(char value)
|
||||
{
|
||||
Write(value);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync([NotNull] char[] buffer, int index, int count)
|
||||
{
|
||||
Write(buffer, index, count);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync(string value)
|
||||
{
|
||||
Write(value);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine()
|
||||
{
|
||||
Buffer.Add(Environment.NewLine);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(string value)
|
||||
{
|
||||
Write(value);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(char value)
|
||||
{
|
||||
WriteLine(value);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(char[] value, int start, int offset)
|
||||
{
|
||||
WriteLine(value, start, offset);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(string value)
|
||||
{
|
||||
WriteLine(value);
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync()
|
||||
{
|
||||
WriteLine();
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(TextWriter writer)
|
||||
{
|
||||
var targetStringCollectionWriter = writer as StringCollectionTextWriter;
|
||||
if (targetStringCollectionWriter != null)
|
||||
{
|
||||
targetStringCollectionWriter.Buffer.Add(Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteList(writer, Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CopyToAsync(TextWriter writer)
|
||||
{
|
||||
var targetStringCollectionWriter = writer as StringCollectionTextWriter;
|
||||
if (targetStringCollectionWriter != null)
|
||||
{
|
||||
targetStringCollectionWriter.Buffer.Add(Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return WriteListAsync(writer, Buffer);
|
||||
}
|
||||
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(string.Empty, Buffer);
|
||||
}
|
||||
|
||||
private static void WriteList(TextWriter writer, BufferEntryCollection values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteListAsync(TextWriter writer, BufferEntryCollection values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
await writer.WriteAsync(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var htmlString = value as HtmlString;
|
||||
if (htmlString != null)
|
||||
{
|
||||
writer.Write(htmlString.ToString());
|
||||
writer.Write(htmlString);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,25 +5,22 @@ using System;
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TextWriter"/> that represents individual write operations as a sequence of strings when buffering.
|
||||
/// The writer is backed by an unbuffered writer. When <c>Flush</c> or <c>FlushAsync</c> is invoked, the writer
|
||||
/// copies all content to the unbuffered writier and switches to writing to the unbuffered writer for all further
|
||||
/// write operations.
|
||||
/// A <see cref="TextWriter"/> that is backed by a unbuffered writer (over the Response stream) and a buffered
|
||||
/// <see cref="StringCollectionTextWriter"/>. When <c>Flush</c> or <c>FlushAsync</c> is invoked, the writer
|
||||
/// copies all content from the buffered writer to the unbuffered one and switches to writing to the unbuffered
|
||||
/// writer for all further write operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is primarily designed to avoid creating large in-memory strings.
|
||||
/// Refer to https://aspnetwebstack.codeplex.com/workitem/585 for more details.
|
||||
/// This type is designed to avoid creating large in-memory strings when buffering and supporting the contract that
|
||||
/// <see cref="RazorPage.FlushAsync"/> expects.
|
||||
/// </remarks>
|
||||
public class RazorTextWriter : TextWriter, IBufferedTextWriter
|
||||
{
|
||||
private static readonly Task _completedTask = Task.FromResult(0);
|
||||
private readonly TextWriter _unbufferedWriter;
|
||||
private readonly Encoding _encoding;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="RazorTextWriter"/>.
|
||||
/// </summary>
|
||||
|
|
@ -32,36 +29,44 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <param name="encoding">The character <see cref="Encoding"/> in which the output is written.</param>
|
||||
public RazorTextWriter(TextWriter unbufferedWriter, Encoding encoding)
|
||||
{
|
||||
_unbufferedWriter = unbufferedWriter;
|
||||
_encoding = encoding;
|
||||
Buffer = new BufferEntryCollection();
|
||||
UnbufferedWriter = unbufferedWriter;
|
||||
BufferedWriter = new StringCollectionTextWriter(encoding);
|
||||
TargetWriter = BufferedWriter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Encoding Encoding
|
||||
{
|
||||
get { return _encoding; }
|
||||
get { return BufferedWriter.Encoding; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsBuffering { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of entries buffered by this instance of <see cref="RazorTextWriter"/>.
|
||||
/// </summary>
|
||||
public BufferEntryCollection Buffer { get; private set; }
|
||||
// Internal for unit testing
|
||||
internal StringCollectionTextWriter BufferedWriter { get; }
|
||||
|
||||
private TextWriter UnbufferedWriter { get; }
|
||||
|
||||
private TextWriter TargetWriter { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(char value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
TargetWriter.Write(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(object value)
|
||||
{
|
||||
var htmlString = value as HtmlString;
|
||||
if (htmlString != null)
|
||||
{
|
||||
Buffer.Add(value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_unbufferedWriter.Write(value);
|
||||
htmlString.WriteTo(TargetWriter);
|
||||
return;
|
||||
}
|
||||
|
||||
base.Write(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -69,25 +74,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
if (count < 0)
|
||||
if (count < 0 || (buffer.Length - index < count))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
if (buffer.Length - index < count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
if (IsBuffering)
|
||||
{
|
||||
Buffer.Add(buffer, index, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unbufferedWriter.Write(buffer, index, count);
|
||||
}
|
||||
TargetWriter.Write(buffer, index, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -95,141 +89,71 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
Buffer.Add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unbufferedWriter.Write(value);
|
||||
}
|
||||
TargetWriter.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync(char value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
Write(value);
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteAsync(value);
|
||||
}
|
||||
return TargetWriter.WriteAsync(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync([NotNull] char[] buffer, int index, int count)
|
||||
{
|
||||
if (IsBuffering)
|
||||
if (index < 0)
|
||||
{
|
||||
Write(buffer, index, count);
|
||||
return _completedTask;
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
else
|
||||
if (count < 0 || (buffer.Length - index < count))
|
||||
{
|
||||
return _unbufferedWriter.WriteAsync(buffer, index, count);
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
return TargetWriter.WriteAsync(buffer, index, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteAsync(string value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
Write(value);
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteAsync(value);
|
||||
}
|
||||
return TargetWriter.WriteAsync(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine()
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
Buffer.Add(Environment.NewLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
_unbufferedWriter.WriteLine();
|
||||
}
|
||||
TargetWriter.WriteLine();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteLine(string value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
Write(value);
|
||||
WriteLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
_unbufferedWriter.WriteLine(value);
|
||||
}
|
||||
TargetWriter.WriteLine(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(char value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
WriteLine(value);
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteLineAsync(value);
|
||||
}
|
||||
return TargetWriter.WriteLineAsync(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(char[] value, int start, int offset)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
WriteLine(value, start, offset);
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteLineAsync(value, start, offset);
|
||||
}
|
||||
return TargetWriter.WriteLineAsync(value, start, offset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync(string value)
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
WriteLine(value);
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteLineAsync(value);
|
||||
}
|
||||
return TargetWriter.WriteLineAsync(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task WriteLineAsync()
|
||||
{
|
||||
if (IsBuffering)
|
||||
{
|
||||
WriteLine();
|
||||
return _completedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _unbufferedWriter.WriteLineAsync();
|
||||
}
|
||||
return TargetWriter.WriteLineAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -242,10 +166,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
if (IsBuffering)
|
||||
{
|
||||
IsBuffering = false;
|
||||
CopyTo(_unbufferedWriter);
|
||||
TargetWriter = UnbufferedWriter;
|
||||
CopyTo(UnbufferedWriter);
|
||||
}
|
||||
|
||||
_unbufferedWriter.Flush();
|
||||
UnbufferedWriter.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -259,60 +184,37 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
if (IsBuffering)
|
||||
{
|
||||
IsBuffering = false;
|
||||
await CopyToAsync(_unbufferedWriter);
|
||||
TargetWriter = UnbufferedWriter;
|
||||
await CopyToAsync(UnbufferedWriter);
|
||||
}
|
||||
|
||||
await _unbufferedWriter.FlushAsync();
|
||||
await UnbufferedWriter.FlushAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(TextWriter writer)
|
||||
{
|
||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||
if (targetRazorTextWriter != null && targetRazorTextWriter.IsBuffering)
|
||||
{
|
||||
targetRazorTextWriter.Buffer.Add(Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the target writer is not buffering, we can directly copy to it's unbuffered writer
|
||||
var targetWriter = targetRazorTextWriter != null ? targetRazorTextWriter._unbufferedWriter : writer;
|
||||
WriteList(targetWriter, Buffer);
|
||||
}
|
||||
writer = UnWrapRazorTextWriter(writer);
|
||||
BufferedWriter.CopyTo(writer);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CopyToAsync(TextWriter writer)
|
||||
{
|
||||
writer = UnWrapRazorTextWriter(writer);
|
||||
return BufferedWriter.CopyToAsync(writer);
|
||||
}
|
||||
|
||||
private static TextWriter UnWrapRazorTextWriter(TextWriter writer)
|
||||
{
|
||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||
if (targetRazorTextWriter != null && targetRazorTextWriter.IsBuffering)
|
||||
if (targetRazorTextWriter != null)
|
||||
{
|
||||
targetRazorTextWriter.Buffer.Add(Buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the target writer is not buffering, we can directly copy to it's unbuffered writer
|
||||
var targetWriter = targetRazorTextWriter != null ? targetRazorTextWriter._unbufferedWriter : writer;
|
||||
return WriteListAsync(targetWriter, Buffer);
|
||||
writer = targetRazorTextWriter.IsBuffering ? targetRazorTextWriter.BufferedWriter :
|
||||
targetRazorTextWriter.UnbufferedWriter;
|
||||
}
|
||||
|
||||
return _completedTask;
|
||||
}
|
||||
|
||||
private static void WriteList(TextWriter writer, BufferEntryCollection values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
writer.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteListAsync(TextWriter writer, BufferEntryCollection values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
await writer.WriteAsync(value);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,13 +61,30 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
_pageExecutionFeature = context.HttpContext.GetFeature<IPageExecutionListenerFeature>();
|
||||
|
||||
if (!_isPartial)
|
||||
if (_isPartial)
|
||||
{
|
||||
await RenderPartialAsync(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bodyWriter = await RenderPageAsync(_razorPage, context, executeViewStart: true);
|
||||
await RenderLayoutAsync(context, bodyWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderPartialAsync(ViewContext context)
|
||||
{
|
||||
if (EnableInstrumentation)
|
||||
{
|
||||
// When instrmenting, we need to Decorate the output in an instrumented writer which
|
||||
// RenderPageAsync does.
|
||||
var bodyWriter = await RenderPageAsync(_razorPage, context, executeViewStart: false);
|
||||
await bodyWriter.CopyToAsync(context.Writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the non-instrumented case, we don't need to buffer contents. For Html.Partial, the writer is
|
||||
// an in memory writer and for Partial views, we directly write to the Response.
|
||||
await RenderPageCoreAsync(_razorPage, context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public class BufferEntryCollectionTest
|
||||
{
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public class StringCollectionTextWriterTest
|
||||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void Write_WritesDataTypes_ToBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new[] { "True", "3", "18446744073709551615", "Hello world", "3.14", "2.718", "m" };
|
||||
var writer = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// Act
|
||||
writer.Write(true);
|
||||
writer.Write(3);
|
||||
writer.Write(ulong.MaxValue);
|
||||
writer.Write(new TestClass());
|
||||
writer.Write(3.14);
|
||||
writer.Write(2.718m);
|
||||
writer.Write('m');
|
||||
|
||||
// Assert
|
||||
Assert.Equal<object>(expected, writer.Buffer.BufferEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void WriteLine_WritesDataTypes_ToBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var newLine = Environment.NewLine;
|
||||
var expected = new List<object> { "False", newLine, "1.1", newLine, "3", newLine };
|
||||
var writer = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// Act
|
||||
writer.WriteLine(false);
|
||||
writer.WriteLine(1.1f);
|
||||
writer.WriteLine(3L);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, writer.Buffer.BufferEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Write_WritesCharBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var input1 = new ArraySegment<char>(new char[] { 'a', 'b', 'c', 'd' }, 1, 3);
|
||||
var input2 = new ArraySegment<char>(new char[] { 'e', 'f' }, 0, 2);
|
||||
var input3 = new ArraySegment<char>(new char[] { 'g', 'h', 'i', 'j' }, 3, 1);
|
||||
var writer = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// Act
|
||||
writer.Write(input1.Array, input1.Offset, input1.Count);
|
||||
await writer.WriteAsync(input2.Array, input2.Offset, input2.Count);
|
||||
await writer.WriteLineAsync(input3.Array, input3.Offset, input3.Count);
|
||||
|
||||
// Assert
|
||||
var buffer = writer.Buffer.BufferEntries;
|
||||
Assert.Equal(4, buffer.Count);
|
||||
Assert.Equal("bcd", buffer[0]);
|
||||
Assert.Equal("ef", buffer[1]);
|
||||
Assert.Equal("j", buffer[2]);
|
||||
Assert.Equal(Environment.NewLine, buffer[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteLines_WritesCharBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var newLine = Environment.NewLine;
|
||||
var writer = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// Act
|
||||
writer.WriteLine();
|
||||
await writer.WriteLineAsync();
|
||||
|
||||
// Assert
|
||||
var actual = writer.Buffer.BufferEntries;
|
||||
Assert.Equal<object>(new[] { newLine, newLine }, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Write_WritesStringBuffer()
|
||||
{
|
||||
// Arrange
|
||||
var newLine = Environment.NewLine;
|
||||
var input1 = "Hello";
|
||||
var input2 = "from";
|
||||
var input3 = "ASP";
|
||||
var input4 = ".Net";
|
||||
var writer = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// Act
|
||||
writer.Write(input1);
|
||||
writer.WriteLine(input2);
|
||||
await writer.WriteAsync(input3);
|
||||
await writer.WriteLineAsync(input4);
|
||||
|
||||
// Assert
|
||||
var actual = writer.Buffer.BufferEntries;
|
||||
Assert.Equal<object>(new[] { input1, input2, newLine, input3, input4, newLine }, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Copy_CopiesContent_IfTargetTextWriterIsAStringCollectionTextWriter()
|
||||
{
|
||||
// Arrange
|
||||
var source = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
var target = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
|
||||
// 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.Buffer.BufferEntries.Count);
|
||||
Assert.Equal(1, target.Buffer.BufferEntries.Count);
|
||||
Assert.Same(source.Buffer.BufferEntries, target.Buffer.BufferEntries[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Copy_WritesContent_IfTargetTextWriterIsNotAStringCollectionTextWriter()
|
||||
{
|
||||
// Arrange
|
||||
var source = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
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());
|
||||
}
|
||||
|
||||
private class TestClass
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "Hello world";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,44 +17,87 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
{
|
||||
private readonly IServiceProvider _services = TestHelper.CreateServices("RazorInstrumentationWebsite");
|
||||
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
|
||||
private readonly string _expected = string.Join(Environment.NewLine,
|
||||
@"<div>",
|
||||
@"2147483647",
|
||||
"",
|
||||
@"viewstart-content",
|
||||
@"<p class=""Hello world"">",
|
||||
@"page-content",
|
||||
@"</p>",
|
||||
@"</div>");
|
||||
private readonly IEnumerable<Tuple<int, int, bool>> _expectedLineMappings = new[]
|
||||
{
|
||||
Tuple.Create(93, 2, true),
|
||||
Tuple.Create(96, 16, false),
|
||||
Tuple.Create(112, 2, true),
|
||||
Tuple.Create(0, 2, true),
|
||||
Tuple.Create(2, 8, true),
|
||||
Tuple.Create(10, 16, false),
|
||||
Tuple.Create(26, 1, true),
|
||||
Tuple.Create(27, 21, true),
|
||||
Tuple.Create(0, 7, true),
|
||||
Tuple.Create(8, 12, false),
|
||||
Tuple.Create(20, 2, true),
|
||||
Tuple.Create(23, 12, false),
|
||||
Tuple.Create(35, 8, true),
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> ActionNames
|
||||
public static IEnumerable<object[]> InstrumentationData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new[] { "FullPath" };
|
||||
yield return new[] { "ViewDiscoveryPath" };
|
||||
var expected = string.Join(Environment.NewLine,
|
||||
@"<div>",
|
||||
@"2147483647",
|
||||
"",
|
||||
@"viewstart-content",
|
||||
@"<p class=""Hello world"">",
|
||||
@"page-content",
|
||||
@"</p>",
|
||||
@"</div>");
|
||||
|
||||
var expectedLineMappings = new[]
|
||||
{
|
||||
Tuple.Create(93, 2, true),
|
||||
Tuple.Create(96, 16, false),
|
||||
Tuple.Create(112, 2, true),
|
||||
Tuple.Create(0, 2, true),
|
||||
Tuple.Create(2, 8, true),
|
||||
Tuple.Create(10, 16, false),
|
||||
Tuple.Create(26, 1, true),
|
||||
Tuple.Create(27, 21, true),
|
||||
Tuple.Create(0, 7, true),
|
||||
Tuple.Create(8, 12, false),
|
||||
Tuple.Create(20, 2, true),
|
||||
Tuple.Create(23, 12, false),
|
||||
Tuple.Create(35, 8, true),
|
||||
};
|
||||
|
||||
yield return new object[] { "FullPath", expected, expectedLineMappings };
|
||||
yield return new object[] { "ViewDiscoveryPath", expected, expectedLineMappings };
|
||||
|
||||
var expected2 = string.Join(Environment.NewLine,
|
||||
"<div>",
|
||||
"2147483647",
|
||||
"",
|
||||
"viewstart-content",
|
||||
"view-with-partial-content",
|
||||
"",
|
||||
@"<p class=""class"">partial-content</p>",
|
||||
"",
|
||||
@"<p class=""class"">partial-content</p>",
|
||||
"</div>");
|
||||
var expectedLineMappings2 = new[]
|
||||
{
|
||||
Tuple.Create(93, 2, true),
|
||||
Tuple.Create(96, 16, false),
|
||||
Tuple.Create(112, 2, true),
|
||||
Tuple.Create(0, 27, true),
|
||||
Tuple.Create(28, 39, false),
|
||||
// Html.PartialAsync()
|
||||
Tuple.Create(29, 4, true),
|
||||
Tuple.Create(33, 8, true),
|
||||
Tuple.Create(41, 4, false),
|
||||
Tuple.Create(45, 1, true),
|
||||
Tuple.Create(46, 20, true),
|
||||
Tuple.Create(67, 2, true),
|
||||
// Html.RenderPartial()
|
||||
Tuple.Create(29, 4, true),
|
||||
Tuple.Create(33, 8, true),
|
||||
Tuple.Create(41, 4, false),
|
||||
Tuple.Create(45, 1, true),
|
||||
Tuple.Create(46, 20, true),
|
||||
Tuple.Create(0, 7, true),
|
||||
Tuple.Create(8, 12, false),
|
||||
Tuple.Create(20, 2, true),
|
||||
Tuple.Create(23, 12, false),
|
||||
Tuple.Create(35, 8, true)
|
||||
};
|
||||
yield return new object[] { "ViewWithPartial", expected2, expectedLineMappings2 };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsAreServedWithoutInstrumentationByDefault(string actionName)
|
||||
[MemberData(nameof(InstrumentationData))]
|
||||
public async Task ViewsAreServedWithoutInstrumentationByDefault(string actionName,
|
||||
string expected,
|
||||
IEnumerable<Tuple<int, int, bool>> expectedLineMappings)
|
||||
{
|
||||
// Arrange
|
||||
var context = new TestPageExecutionContext();
|
||||
|
|
@ -66,13 +109,15 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsAreInstrumentedWhenPageExecutionListenerFeatureIsEnabled(string actionName)
|
||||
[MemberData(nameof(InstrumentationData))]
|
||||
public async Task ViewsAreInstrumentedWhenPageExecutionListenerFeatureIsEnabled(string actionName,
|
||||
string expected,
|
||||
IEnumerable<Tuple<int, int, bool>> expectedLineMappings)
|
||||
{
|
||||
// Arrange
|
||||
var context = new TestPageExecutionContext();
|
||||
|
|
@ -85,13 +130,15 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
Assert.Equal(expected, body.Trim());
|
||||
Assert.Equal(expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsCanSwitchFromRegularToInstrumented(string actionName)
|
||||
[MemberData(nameof(InstrumentationData))]
|
||||
public async Task ViewsCanSwitchFromRegularToInstrumented(string actionName,
|
||||
string expected,
|
||||
IEnumerable<Tuple<int, int, bool>> expectedLineMappings)
|
||||
{
|
||||
// Arrange - 1
|
||||
var context = new TestPageExecutionContext();
|
||||
|
|
@ -103,7 +150,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert - 1
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
|
||||
// Arrange - 2
|
||||
|
|
@ -113,14 +160,30 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert - 2
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
Assert.Equal(expected, body.Trim());
|
||||
Assert.Equal(expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SwitchingFromNonInstrumentedToInstrumentedWorksForLayoutAndViewStarts()
|
||||
{
|
||||
// Arrange - 1
|
||||
var expectedLineMappings = new[]
|
||||
{
|
||||
Tuple.Create(93, 2, true),
|
||||
Tuple.Create(96, 16, false),
|
||||
Tuple.Create(112, 2, true),
|
||||
Tuple.Create(0, 2, true),
|
||||
Tuple.Create(2, 8, true),
|
||||
Tuple.Create(10, 16, false),
|
||||
Tuple.Create(26, 1, true),
|
||||
Tuple.Create(27, 21, true),
|
||||
Tuple.Create(0, 7, true),
|
||||
Tuple.Create(8, 12, false),
|
||||
Tuple.Create(20, 2, true),
|
||||
Tuple.Create(23, 12, false),
|
||||
Tuple.Create(35, 8, true),
|
||||
};
|
||||
var context = new TestPageExecutionContext();
|
||||
var services = GetServiceProvider(context);
|
||||
var server = TestServer.Create(services, _app);
|
||||
|
|
@ -130,7 +193,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var body = await client.GetStringAsync("http://localhost/Home/FullPath");
|
||||
|
||||
// Assert - 1
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
|
||||
// Arrange - 2
|
||||
|
|
@ -140,8 +202,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
body = await client.GetStringAsync("http://localhost/Home/ViewDiscoveryPath");
|
||||
|
||||
// Assert - 2
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
Assert.Equal(expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
private IServiceProvider GetServiceProvider(TestPageExecutionContext pageExecutionContext)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
|
|
@ -524,6 +525,32 @@ Layout end
|
|||
context.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Write_WithHtmlString_WritesValueWithoutEncoding()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new RazorTextWriter(TextWriter.Null, Encoding.UTF8);
|
||||
var stringCollectionWriter = new StringCollectionTextWriter(Encoding.UTF8);
|
||||
stringCollectionWriter.Write("text1");
|
||||
stringCollectionWriter.Write("text2");
|
||||
|
||||
var page = CreatePage(p =>
|
||||
{
|
||||
p.Write(new HtmlString("Hello world"));
|
||||
p.Write(new HtmlString(stringCollectionWriter));
|
||||
});
|
||||
page.ViewContext.Writer = writer;
|
||||
|
||||
// Act
|
||||
await page.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
var buffer = writer.BufferedWriter.Buffer;
|
||||
Assert.Equal(2, buffer.BufferEntries.Count);
|
||||
Assert.Equal("Hello world", buffer.BufferEntries[0]);
|
||||
Assert.Same(stringCollectionWriter.Buffer.BufferEntries, buffer.BufferEntries[1]);
|
||||
}
|
||||
|
||||
private static TestableRazorPage CreatePage(Action<TestableRazorPage> executeAction,
|
||||
ViewContext context = null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
writer.Write('m');
|
||||
|
||||
// Assert
|
||||
Assert.Equal<object>(expected, writer.Buffer.BufferEntries);
|
||||
Assert.Equal<object>(expected, writer.BufferedWriter.Buffer.BufferEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
writer.Write(2.718m);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(writer.Buffer.BufferEntries);
|
||||
Assert.Empty(writer.BufferedWriter.Buffer.BufferEntries);
|
||||
foreach (var item in expected)
|
||||
{
|
||||
unbufferedWriter.Verify(v => v.Write(item), Times.Once());
|
||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
await writer.WriteLineAsync(buffer1);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(writer.Buffer.BufferEntries);
|
||||
Assert.Empty(writer.BufferedWriter.Buffer.BufferEntries);
|
||||
unbufferedWriter.Verify(v => v.Write('x'), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.Write(buffer1, 1, 2), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.Write(buffer1, 0, 4), Times.Once());
|
||||
|
|
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
await writer.WriteLineAsync("gh");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(writer.Buffer.BufferEntries);
|
||||
Assert.Empty(writer.BufferedWriter.Buffer.BufferEntries);
|
||||
unbufferedWriter.Verify(v => v.Write("a"), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.WriteLine("ab"), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.WriteAsync("ef"), Times.Once());
|
||||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
writer.WriteLine(3L);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, writer.Buffer.BufferEntries);
|
||||
Assert.Equal(expected, writer.BufferedWriter.Buffer.BufferEntries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -146,7 +146,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
writer.WriteLine(3L);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(writer.Buffer.BufferEntries);
|
||||
Assert.Empty(writer.BufferedWriter.Buffer.BufferEntries);
|
||||
unbufferedWriter.Verify(v => v.Write("False"), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.Write("1.1"), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.Write("3"), Times.Once());
|
||||
|
|
@ -168,7 +168,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
await writer.WriteLineAsync(input3.Array, input3.Offset, input3.Count);
|
||||
|
||||
// Assert
|
||||
var buffer = writer.Buffer.BufferEntries;
|
||||
var buffer = writer.BufferedWriter.Buffer.BufferEntries;
|
||||
Assert.Equal(4, buffer.Count);
|
||||
Assert.Equal("bcd", buffer[0]);
|
||||
Assert.Equal("ef", buffer[1]);
|
||||
|
|
@ -188,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
await writer.WriteLineAsync();
|
||||
|
||||
// Assert
|
||||
var actual = writer.Buffer.BufferEntries;
|
||||
var actual = writer.BufferedWriter.Buffer.BufferEntries;
|
||||
Assert.Equal<object>(new[] { newLine, newLine }, actual);
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
await writer.WriteLineAsync(input4);
|
||||
|
||||
// Assert
|
||||
var actual = writer.Buffer.BufferEntries;
|
||||
var actual = writer.BufferedWriter.Buffer.BufferEntries;
|
||||
Assert.Equal<object>(new[] { input1, input2, newLine, input3, input4, newLine }, actual);
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +228,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
// Make sure content was written to the source.
|
||||
Assert.Equal(2, source.Buffer.BufferEntries.Count);
|
||||
Assert.Equal(1, target.Buffer.BufferEntries.Count);
|
||||
Assert.Same(source.Buffer.BufferEntries, target.Buffer.BufferEntries[0]);
|
||||
Assert.Equal(2, source.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Equal(1, target.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Same(source.BufferedWriter.Buffer.BufferEntries, target.BufferedWriter.Buffer.BufferEntries[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -249,8 +249,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
// Make sure content was written to the source.
|
||||
Assert.Equal(2, source.Buffer.BufferEntries.Count);
|
||||
Assert.Empty(target.Buffer.BufferEntries);
|
||||
Assert.Equal(2, source.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Empty(target.BufferedWriter.Buffer.BufferEntries);
|
||||
unbufferedWriter.Verify(v => v.Write("Hello world"), Times.Once());
|
||||
unbufferedWriter.Verify(v => v.Write("bc"), Times.Once());
|
||||
}
|
||||
|
|
@ -286,9 +286,9 @@ abc";
|
|||
await source.CopyToAsync(target);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, source.Buffer.BufferEntries.Count);
|
||||
Assert.Equal(1, target.Buffer.BufferEntries.Count);
|
||||
Assert.Same(source.Buffer.BufferEntries, target.Buffer.BufferEntries[0]);
|
||||
Assert.Equal(3, source.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Equal(1, target.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Same(source.BufferedWriter.Buffer.BufferEntries, target.BufferedWriter.Buffer.BufferEntries[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -307,8 +307,8 @@ abc";
|
|||
|
||||
// Assert
|
||||
// Make sure content was written to the source.
|
||||
Assert.Equal(3, source.Buffer.BufferEntries.Count);
|
||||
Assert.Empty(target.Buffer.BufferEntries);
|
||||
Assert.Equal(3, source.BufferedWriter.Buffer.BufferEntries.Count);
|
||||
Assert.Empty(target.BufferedWriter.Buffer.BufferEntries);
|
||||
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());
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_AsPartial_DoesNotCreateOutputBuffer()
|
||||
public async Task RenderAsync_AsPartial_DoesNotBufferOutput()
|
||||
{
|
||||
// Arrange
|
||||
TextWriter actual = null;
|
||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
Assert.Equal("Hello world", expected.ToString());
|
||||
Assert.Equal("Hello world", viewContext.Writer.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -653,20 +653,26 @@ section-content-2";
|
|||
public async Task RenderAsync_UsesPageExecutionFeatureFromRequest_ToGetExecutionContext()
|
||||
{
|
||||
// Arrange
|
||||
var writer = Mock.Of<TextWriter>();
|
||||
var writer = new StringWriter();
|
||||
var executed = false;
|
||||
var feature = new Mock<IPageExecutionListenerFeature>(MockBehavior.Strict);
|
||||
|
||||
var pageContext = Mock.Of<IPageExecutionContext>();
|
||||
feature.Setup(f => f.GetContext("/MyPartialPage.cshtml", writer))
|
||||
feature.Setup(f => f.GetContext("/MyPartialPage.cshtml", It.IsAny<RazorTextWriter>()))
|
||||
.Returns(pageContext)
|
||||
.Verifiable();
|
||||
|
||||
feature.Setup(f => f.DecorateWriter(It.IsAny<RazorTextWriter>()))
|
||||
.Returns((RazorTextWriter r) => r)
|
||||
.Verifiable();
|
||||
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
Assert.Same(writer, v.Output);
|
||||
Assert.IsType<RazorTextWriter>(v.Output);
|
||||
Assert.Same(pageContext, v.PageExecutionContext);
|
||||
executed = true;
|
||||
|
||||
v.Write("Hello world");
|
||||
});
|
||||
page.Path = "/MyPartialPage.cshtml";
|
||||
|
||||
|
|
@ -684,6 +690,7 @@ section-content-2";
|
|||
// Assert
|
||||
feature.Verify();
|
||||
Assert.True(executed);
|
||||
Assert.Equal("Hello world", viewContext.Writer.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
|
|
@ -17,5 +17,10 @@ namespace RazorInstrumentationWebSite
|
|||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public ActionResult ViewWithPartial()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
view-with-partial-content
|
||||
@await Html.PartialAsync("_PartialView")
|
||||
@{
|
||||
await Html.RenderPartialAsync("_PartialView");
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
var cls = "class";
|
||||
}
|
||||
<p class="@cls">partial-content</p>
|
||||
Loading…
Reference in New Issue