Add `HtmlEncoder` parameter to `RazorPage.StartTagHelperWritingScope()`

- aspnet/Razor#643 part 2: react to aspnet/Razor#653
 - change test calls and delegates to include new parameter
 - add tests specifically of the new feature
 - add tag helpers using new feature to `TagHelpersTest` functional test
- note `HtmlEncoder`s used elsewhere e.g. in other `RazorPage` instances are unaffected
 - i.e. changing one `RazorPage.HtmlEncoder` property only affects C# expressions in that page

Also simplify scope management, removing bizarre assertion when user does something odd.

nits:
- correct `// Act` and `//  Act & Assert` content
- remove #YOLO wrapping
This commit is contained in:
Doug Bunting 2016-01-17 22:59:59 -08:00
parent 249673f2bc
commit 2810858905
39 changed files with 617 additions and 184 deletions

View File

@ -30,8 +30,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public abstract class RazorPage : IRazorPage
{
private readonly HashSet<string> _renderedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<HtmlContentWrapperTextWriter> _writerScopes;
private TextWriter _originalWriter;
private readonly Stack<TagHelperScopeInfo> _tagHelperScopes = new Stack<TagHelperScopeInfo>();
private IUrlHelper _urlHelper;
private ITagHelperActivator _tagHelperActivator;
private ITypeActivatorCache _typeActivatorCache;
@ -44,7 +43,6 @@ namespace Microsoft.AspNet.Mvc.Razor
public RazorPage()
{
SectionWriters = new Dictionary<string, RenderAsyncDelegate>(StringComparer.OrdinalIgnoreCase);
_writerScopes = new Stack<HtmlContentWrapperTextWriter>();
}
/// <summary>
@ -62,7 +60,8 @@ namespace Microsoft.AspNet.Mvc.Razor
public string Layout { get; set; }
/// <summary>
/// Gets the <see cref="HtmlEncoder"/> to be used for encoding HTML.
/// Gets the <see cref="System.Text.Encodings.Web.HtmlEncoder"/> to use when this <see cref="RazorPage"/>
/// handles non-<see cref="IHtmlContent"/> C# expressions.
/// </summary>
[RazorInject]
public HtmlEncoder HtmlEncoder { get; set; }
@ -116,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public IDictionary<string, RenderAsyncDelegate> PreviousSectionWriters { get; set; }
/// <inheritdoc />
public IDictionary<string, RenderAsyncDelegate> SectionWriters { get; private set; }
public IDictionary<string, RenderAsyncDelegate> SectionWriters { get; }
/// <inheritdoc />
public abstract Task ExecuteAsync();
@ -201,28 +200,31 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <summary>
/// Starts a new writing scope.
/// Starts a new writing scope and optionally overrides <see cref="HtmlEncoder"/> within that scope.
/// </summary>
/// <param name="encoder">
/// The <see cref="System.Text.Encodings.Web.HtmlEncoder"/> to use when this <see cref="RazorPage"/> handles
/// non-<see cref="IHtmlContent"/> C# expressions. If <c>null</c>, does not change <see cref="HtmlEncoder"/>.
/// </param>
/// <remarks>
/// All writes to the <see cref="Output"/> or <see cref="ViewContext.Writer"/> after calling this method will
/// be buffered until <see cref="EndTagHelperWritingScope"/> is called.
/// </remarks>
public void StartTagHelperWritingScope()
public void StartTagHelperWritingScope(HtmlEncoder encoder)
{
// If there isn't a base writer take the ViewContext.Writer
if (_originalWriter == null)
{
_originalWriter = ViewContext.Writer;
}
_tagHelperScopes.Push(new TagHelperScopeInfo(HtmlEncoder, ViewContext.Writer));
var buffer = new ViewBuffer(BufferScope, Path);
var writer = new HtmlContentWrapperTextWriter(buffer, _originalWriter.Encoding);
// If passed an HtmlEncoder, override the property.
if (encoder != null)
{
HtmlEncoder = encoder;
}
// We need to replace the ViewContext's Writer to ensure that all content (including content written
// from HTML helpers) is redirected.
var buffer = new ViewBuffer(BufferScope, Path);
var writer = new HtmlContentWrapperTextWriter(buffer, ViewContext.Writer.Encoding);
ViewContext.Writer = writer;
_writerScopes.Push(writer);
}
/// <summary>
@ -231,28 +233,21 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <returns>The buffered <see cref="TagHelperContent"/>.</returns>
public TagHelperContent EndTagHelperWritingScope()
{
if (_writerScopes.Count == 0)
if (_tagHelperScopes.Count == 0)
{
throw new InvalidOperationException(Resources.RazorPage_ThereIsNoActiveWritingScopeToEnd);
}
var writer = _writerScopes.Pop();
Debug.Assert(writer == ViewContext.Writer);
if (_writerScopes.Count > 0)
{
ViewContext.Writer = _writerScopes.Peek();
}
else
{
ViewContext.Writer = _originalWriter;
// No longer a base writer
_originalWriter = null;
}
// Get the content written during the current scope.
var writer = ViewContext.Writer as HtmlContentWrapperTextWriter;
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml(writer.ContentBuilder);
tagHelperContent.AppendHtml(writer?.ContentBuilder);
// Restore previous scope.
var scopeInfo = _tagHelperScopes.Pop();
HtmlEncoder = scopeInfo.Encoder;
ViewContext.Writer = scopeInfo.Writer;
return tagHelperContent;
}
@ -290,7 +285,9 @@ namespace Microsoft.AspNet.Mvc.Razor
/// 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="HtmlEncoder"/> to use when encoding <paramref name="value"/>.</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
@ -825,9 +822,8 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </remarks>
public async Task<HtmlString> FlushAsync()
{
// If there are active writing scopes then we should throw. Cannot flush content that has the potential to
// change.
if (_writerScopes.Count > 0)
// If there are active scopes, then we should throw. Cannot flush content that has the potential to change.
if (_tagHelperScopes.Count > 0)
{
throw new InvalidOperationException(
Resources.FormatRazorPage_CannotFlushWhileInAWritingScope(nameof(FlushAsync), Path));
@ -1000,5 +996,18 @@ namespace Microsoft.AspNet.Mvc.Razor
public bool Suppressed { get; set; }
}
private struct TagHelperScopeInfo
{
public TagHelperScopeInfo(HtmlEncoder encoder, TextWriter writer)
{
Encoder = encoder;
Writer = writer;
}
public HtmlEncoder Encoder { get; }
public TextWriter Writer { get; }
}
}
}

View File

@ -253,6 +253,33 @@ page:<root>root-content</root>"
PlatformNormalizer.NormalizeContent(expectedContent),
responseContent,
ignoreLineEndingDifferences: true);
#endif
}
[Theory]
[InlineData("Index")]
[InlineData("CustomEncoder")]
[InlineData("NullEncoder")]
[InlineData("TwoEncoders")]
[InlineData("ThreeEncoders")]
public async Task EncodersPages_ReturnExpectedContent(string actionName)
{
// Arrange
var outputFile = $"compiler/resources/TagHelpersWebSite.Encoders.{ actionName }.html";
var expectedContent =
await ResourceFile.ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false);
// Act
var response = await Client.GetAsync($"/Encoders/{ actionName }");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseContent = await response.Content.ReadAsStringAsync();
#if GENERATE_BASELINES
ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent);
#else
Assert.Equal(expectedContent, responseContent, ignoreLineEndingDifferences: true);
#endif
}
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Using a custom HTML encoder - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - Using a custom HTML encoder</h1>
<div>
<pre>Custom encoder: Custom[[Outer knows <b>1 < 4</b>]]
<inner>Custom[[Inner knows <b>1 < 4</b>]]</inner><br />Outer knows &lt;b&gt;2 &lt; 4&lt;/b&gt;
<inner>Inner knows &lt;b&gt;2 &lt; 4&lt;/b&gt;</inner></pre>
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>Using the default HTML encoder - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - Using the default HTML encoder</h1>
<div>
<pre>Default encoder: Outer knows &lt;b&gt;1 &lt; 4&lt;/b&gt;
<pre>Default encoder: Inner knows &lt;b&gt;1 &lt; 4&lt;/b&gt;</pre></pre>
</div>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Using the null HTML encoder - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - Using the null HTML encoder</h1>
<div>
<pre>Outer knows &lt;b&gt;2 &lt; 4&lt;/b&gt;
<inner>Inner knows &lt;b&gt;2 &lt; 4&lt;/b&gt;</inner><br />Null encoder: Outer knows <b>1 < 4</b>
<inner>Inner knows <b>1 < 4</b></inner></pre>
</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>Using three HTML encoders - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - Using three HTML encoders</h1>
<div>
<pre>Custom encoder: Custom[[Outer knows <b>1 < 4</b>]]
<pre>Default encoder: Custom[[Inner knows <b>1 < 4</b>]]</pre><br />Default encoder: Outer knows &lt;b&gt;2 &lt; 4&lt;/b&gt;
<pre>Default encoder: Inner knows &lt;b&gt;2 &lt; 4&lt;/b&gt;</pre><br />Null encoder: Outer knows <b>3 < 4</b>
<pre>Default encoder: Inner knows <b>3 < 4</b></pre></pre>
</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>Using two HTML encoders - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - Using two HTML encoders</h1>
<div>
<pre>Custom encoder: Custom[[Outer knows <b>1 < 4</b>]]
<inner>Custom[[Inner knows <b>1 < 4</b>]]</inner><br />Outer knows &lt;b&gt;3 &lt; 4&lt;/b&gt;
<inner>Inner knows &lt;b&gt;3 &lt; 4&lt;/b&gt;</inner><br />Null encoder: Outer knows <b>2 < 4</b>
<inner>Inner knows <b>2 < 4</b></inner></pre>
</div>
</body>
</html>

View File

@ -5,6 +5,7 @@ using System;
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.Http.Internal;
@ -41,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
v.HtmlEncoder = new HtmlTestEncoder();
v.Write("Hello Prefix");
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
v.Write("Hello from Output");
v.ViewContext.Writer.Write("Hello from view context writer");
var scopeValue = v.EndTagHelperWritingScope();
@ -54,8 +55,10 @@ namespace Microsoft.AspNet.Mvc.Razor
var pageOutput = page.Output.ToString();
// Assert
Assert.Equal("HtmlEncode[[Hello Prefix]]HtmlEncode[[From Scope: ]]HtmlEncode[[Hello from Output]]" +
"Hello from view context writer", pageOutput);
Assert.Equal(
"HtmlEncode[[Hello Prefix]]HtmlEncode[[From Scope: ]]HtmlEncode[[Hello from Output]]" +
"Hello from view context writer",
pageOutput);
}
[Fact]
@ -67,7 +70,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
v.HtmlEncoder = new HtmlTestEncoder();
v.Write("Hello Prefix");
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
v.Write("Hello In Scope");
var scopeValue = v.EndTagHelperWritingScope();
v.Write("From Scope: ");
@ -91,10 +94,10 @@ namespace Microsoft.AspNet.Mvc.Razor
{
v.HtmlEncoder = new HtmlTestEncoder();
v.Write("Hello Prefix");
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
v.Write("Hello In Scope Pre Nest");
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
v.Write("Hello In Nested Scope");
var scopeValue1 = v.EndTagHelperWritingScope();
@ -108,36 +111,85 @@ namespace Microsoft.AspNet.Mvc.Razor
// Act
await page.ExecuteAsync();
var pageOutput = page.Output.ToString();
// Assert
Assert.Equal("HtmlEncode[[Hello Prefix]]HtmlEncode[[From Scopes: ]]HtmlEncode[[Hello In Scope Pre Nest]]" +
"HtmlEncode[[Hello In Scope Post Nest]]HtmlEncode[[Hello In Nested Scope]]", pageOutput);
var pageOutput = page.Output.ToString();
Assert.Equal(
"HtmlEncode[[Hello Prefix]]HtmlEncode[[From Scopes: ]]HtmlEncode[[Hello In Scope Pre Nest]]" +
"HtmlEncode[[Hello In Scope Post Nest]]HtmlEncode[[Hello In Nested Scope]]",
pageOutput);
}
[Fact]
public async Task StartNewWritingScope_CannotFlushInWritingScope()
public async Task StartTagHelperWritingScope_CannotFlushInWritingScope()
{
// Arrange
var viewContext = CreateViewContext();
var page = CreatePage(async v =>
{
v.Path = "/Views/TestPath/Test.cshtml";
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
await v.FlushAsync();
});
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => page.ExecuteAsync());
Assert.Equal(
"The FlushAsync operation cannot be performed while " +
"inside a writing scope in '/Views/TestPath/Test.cshtml'.",
ex.Message);
}
public static TheoryData<HtmlEncoder> HtmlEncoderData
{
get
{
return new TheoryData<HtmlEncoder>
{
HtmlEncoder.Default,
NullHtmlEncoder.Default,
new HtmlTestEncoder(),
};
}
}
[Theory]
[MemberData(nameof(HtmlEncoderData))]
public async Task StartTagHelperWritingScope_SetsHtmlEncoder(HtmlEncoder encoder)
{
// Arrange
var page = CreatePage(v =>
{
v.StartTagHelperWritingScope(encoder);
});
// Act
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => page.ExecuteAsync());
await page.ExecuteAsync();
// Assert
Assert.Equal("The FlushAsync operation cannot be performed while " +
"inside a writing scope in '/Views/TestPath/Test.cshtml'.", ex.Message);
Assert.Same(encoder, page.HtmlEncoder);
}
[Fact]
public async Task StartNewWritingScope_CannotEndWritingScopeWhenNoWritingScope()
public async Task StartTagHelperWritingScope_DoesNotSetHtmlEncoderToNull()
{
// Arrange
var page = CreatePage(v =>
{
v.StartTagHelperWritingScope(encoder: null);
});
var originalEncoder = page.HtmlEncoder;
// Act
await page.ExecuteAsync();
// Assert
Assert.NotNull(originalEncoder);
Assert.Same(originalEncoder, page.HtmlEncoder);
}
[Fact]
public async Task EndTagHelperWritingScope_CannotEndWritingScopeWhenNoWritingScope()
{
// Arrange
var viewContext = CreateViewContext();
@ -146,11 +198,8 @@ namespace Microsoft.AspNet.Mvc.Razor
v.EndTagHelperWritingScope();
});
// Act
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => page.ExecuteAsync());
// Assert
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => page.ExecuteAsync());
Assert.Equal("There is no active writing scope to end.", ex.Message);
}
@ -159,12 +208,10 @@ namespace Microsoft.AspNet.Mvc.Razor
{
// Arrange
var viewContext = CreateViewContext();
// Act
var page = CreatePage(v =>
{
v.HtmlEncoder = new HtmlTestEncoder();
v.StartTagHelperWritingScope();
v.StartTagHelperWritingScope(encoder: null);
v.Write("Hello World!");
var returnValue = v.EndTagHelperWritingScope();
@ -172,6 +219,8 @@ namespace Microsoft.AspNet.Mvc.Razor
var content = Assert.IsType<DefaultTagHelperContent>(returnValue);
Assert.Equal("HtmlEncode[[Hello World!]]", content.GetContent());
});
// Act & Assert
await page.ExecuteAsync();
}
@ -186,11 +235,8 @@ namespace Microsoft.AspNet.Mvc.Razor
v.DefineSection("qux", _nullRenderAsyncDelegate);
});
// Act
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
() => page.ExecuteAsync());
// Assert
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => page.ExecuteAsync());
Assert.Equal("Section 'qux' is already defined.", ex.Message);
}
@ -227,10 +273,8 @@ namespace Microsoft.AspNet.Mvc.Razor
ex = Assert.Throws<InvalidOperationException>(() => v.RenderSection("bar"));
});
// Act
// Act & Assert
await page.ExecuteAsync();
// Assert
Assert.Equal("RenderSection invocation in '/Views/TestPath/Test.cshtml' is invalid. " +
"RenderSection can only be called from a layout page.",
ex.Message);
@ -250,10 +294,8 @@ namespace Microsoft.AspNet.Mvc.Razor
{ "baz", _nullRenderAsyncDelegate }
};
// Act
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => page.ExecuteAsync());
// Assert
Assert.Equal("Section 'bar' is not defined in path '/Views/TestPath/Test.cshtml'.", ex.Message);
}
@ -265,9 +307,9 @@ namespace Microsoft.AspNet.Mvc.Razor
{
v.Path = "/Views/TestPath/Test.cshtml";
});
// Act and Assert
page.ExecuteAsync();
// Act & Assert
ExceptionAssert.Throws<InvalidOperationException>(() => page.IsSectionDefined("foo"),
"IsSectionDefined invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" IsSectionDefined can only be called from a layout page.");
@ -337,12 +379,12 @@ namespace Microsoft.AspNet.Mvc.Razor
{ "header", _nullRenderAsyncDelegate }
};
// Act
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(page.ExecuteAsync);
// Assert
Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.", ex.Message);
Assert.Equal(
"RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.",
ex.Message);
}
[Fact]
@ -361,12 +403,12 @@ namespace Microsoft.AspNet.Mvc.Razor
{ "header", _nullRenderAsyncDelegate }
};
// Act
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(page.ExecuteAsync);
// Assert
Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.", ex.Message);
Assert.Equal(
"RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.",
ex.Message);
}
[Fact]
@ -385,12 +427,12 @@ namespace Microsoft.AspNet.Mvc.Razor
{ "header", _nullRenderAsyncDelegate }
};
// Act
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(page.ExecuteAsync);
// Assert
Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.", ex.Message);
Assert.Equal(
"RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." +
" The section 'header' has already been rendered.",
ex.Message);
}
[Fact]
@ -404,12 +446,12 @@ namespace Microsoft.AspNet.Mvc.Razor
await v.RenderSectionAsync("header");
});
// Act
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(page.ExecuteAsync);
// Assert
Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid. " +
"RenderSectionAsync can only be called from a layout page.", ex.Message);
Assert.Equal(
"RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid. " +
"RenderSectionAsync can only be called from a layout page.",
ex.Message);
}
[Fact]
@ -422,12 +464,10 @@ namespace Microsoft.AspNet.Mvc.Razor
});
page.Path = path;
page.BodyContent = new HtmlString("some content");
// Act
await page.ExecuteAsync();
var ex = Assert.Throws<InvalidOperationException>(() => page.EnsureRenderedBodyOrSections());
// Assert
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => page.EnsureRenderedBodyOrSections());
Assert.Equal($"RenderBody has not been called for the page at '{path}'.", ex.Message);
}
@ -446,14 +486,14 @@ namespace Microsoft.AspNet.Mvc.Razor
{
{ sectionName, _nullRenderAsyncDelegate }
};
// Act
await page.ExecuteAsync();
var ex = Assert.Throws<InvalidOperationException>(() => page.EnsureRenderedBodyOrSections());
// Assert
Assert.Equal("The following sections have been defined but have not been rendered by the page at " +
$"'{path}': '{sectionName}'.", ex.Message);
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => page.EnsureRenderedBodyOrSections());
Assert.Equal(
"The following sections have been defined but have not been rendered by the page at " +
$"'{path}': '{sectionName}'.",
ex.Message);
}
[Fact]
@ -474,7 +514,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{ sectionB, _nullRenderAsyncDelegate },
};
// Act and Assert
// Act & Assert (does not throw)
await page.ExecuteAsync();
page.EnsureRenderedBodyOrSections();
}
@ -593,7 +633,7 @@ namespace Microsoft.AspNet.Mvc.Razor
await p.FlushAsync();
}, context);
// Act and Assert
// Act & Assert
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => page.ExecuteAsync());
Assert.Equal(expected, ex.Message);
}
@ -923,7 +963,7 @@ namespace Microsoft.AspNet.Mvc.Razor
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
@ -964,7 +1004,7 @@ namespace Microsoft.AspNet.Mvc.Razor
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
@ -992,7 +1032,7 @@ namespace Microsoft.AspNet.Mvc.Razor
items: new Dictionary<object, object>(),
uniqueId: string.Empty,
executeChildContentAsync: () => Task.FromResult(result: true),
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act

View File

@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", url }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(urlHelper => urlHelper.Content(It.IsAny<string>()))
@ -103,7 +103,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", url }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(urlHelper => urlHelper.Content(It.IsAny<string>()))
@ -158,7 +158,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", url }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(urlHelper => urlHelper.Content(It.IsAny<string>()))
@ -213,7 +213,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", url }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(urlHelper => urlHelper.Content(It.IsAny<string>()))
@ -251,7 +251,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", true }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var tagHelper = new UrlResolutionTagHelper(urlHelperFactory: null, htmlEncoder: null);
var context = new TagHelperContext(
@ -288,7 +288,7 @@ namespace Microsoft.AspNet.Mvc.Razor.TagHelpers
{
{ "href", new HtmlString(relativeUrl) }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var urlHelperMock = new Mock<IUrlHelper>();
urlHelperMock
.Setup(urlHelper => urlHelper.Content(It.IsAny<string>()))

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "id", "myanchor" },
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something Else");
@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"a",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -148,7 +148,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"a",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -209,7 +209,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "href", "http://www.contoso.com" }
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
if (propertyName == "asp-route-")
{
anchorTagHelper.RouteValues.Add("name", "value");
@ -255,7 +255,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"a",
attributes: new TagHelperAttributeList(),
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var expectedErrorMessage = "Cannot determine an 'href' attribute for <a>. An <a> with a specified " +
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";

View File

@ -740,7 +740,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
"cache",
new TagHelperAttributeList { { "attr", "value" } },
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
TagHelperContent tagHelperContent;
if (!cache.TryGetValue("key1", out tagHelperContent))
@ -806,7 +806,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetHtmlContent(childContent);

View File

@ -143,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Test
return new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent(childContent);

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "id", "myform" },
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something Else");
@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -168,7 +168,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -239,7 +239,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -290,7 +290,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -357,7 +357,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "aCTiON", "my-action" },
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -397,7 +397,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "action", "my-action" },
},
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
if (propertyName == "asp-route-")
{
formTagHelper.RouteValues.Add("name", "value");
@ -438,7 +438,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: _ => Task.FromResult<TagHelperContent>(null));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";

View File

@ -54,7 +54,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"img",
outputAttributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
var urlHelper = new Mock<IUrlHelper>();
@ -294,7 +295,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new TagHelperOutput(
"img",
attributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent(default(string));

View File

@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
originalTagName,
outputAttributes,
getChildContentAsync: useCachedResult => Task.FromResult<TagHelperContent>(result: null))
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
{
TagMode = TagMode.SelfClosing,
};
@ -204,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -268,7 +268,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
originalTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -367,7 +367,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -468,7 +468,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -566,7 +566,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -675,7 +675,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -792,7 +792,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()))
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};
@ -875,7 +876,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()))
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};

View File

@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
htmlAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml(tagHelperOutputContent.OriginalChildContent);

View File

@ -844,7 +844,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
}
private static IHostingEnvironment MakeHostingEnvironment()

View File

@ -408,7 +408,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagHelperOutput.TagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
// GetChildContentAsync should not be invoked since we are setting the content below.
Assert.True(false);
@ -478,7 +478,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
originalTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent(originalContent);
@ -542,7 +542,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
originalTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent(originalContent);
@ -574,7 +574,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
tagHelperOutput.Content.SetContent(content);
return tagHelperOutput;

View File

@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "form",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult) =>
getChildContentAsync: (useCachedResult, encoder) =>
{
Assert.True(viewContext.FormContext.CanRenderAtEndOfForm);
foreach (var item in tagBuilderList)
@ -103,7 +103,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return Task.FromResult(true);
},
startTagHelperWritingScope: () => { },
startTagHelperWritingScope: _ => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// This TagHelper will pre-execute the child content forcing the body to be cached.

View File

@ -847,7 +847,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new TagHelperOutput(
tagName,
attributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
}
private static IHostingEnvironment MakeHostingEnvironment()

View File

@ -208,7 +208,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -297,7 +297,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml("Something");
@ -401,7 +401,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml("Something");
@ -489,7 +489,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -570,7 +570,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName,
originalAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");

View File

@ -336,7 +336,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(outputAttributes),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var context = new TagHelperContext(
allAttributes,
items: new Dictionary<object, object>(),
@ -436,7 +437,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var context = new TagHelperContext(
allAttributes,
items: new Dictionary<object, object>(),
@ -458,7 +460,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
"p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.Append("Something");
@ -492,7 +494,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ attributeName, "world2" }
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.Append("Something Else");
@ -523,7 +525,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
"p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.Append("Something");
@ -555,7 +557,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ "route-Hello", "World" },
{ "Route-I", "Am" }
},
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedAttribute = new TagHelperAttribute("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute);
@ -582,7 +585,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ "route-Hello", "World" },
{ "Route-I", "Am" }
},
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedAttribute = new TagHelperAttribute("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute);
var attributes = tagHelperOutput.Attributes
@ -607,7 +611,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ "route-Hello", "World" },
{ "Route-I", "Am" }
},
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedAttribute = new TagHelperAttribute("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute);
@ -765,7 +770,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
"p",
outputAttributes,
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var tagBuilder = new TagBuilder("p");
foreach (var attr in tagBuilderAttributes)
@ -790,7 +796,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedAttribute = new TagHelperAttribute("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute);
@ -812,7 +819,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
tagHelperOutput.Attributes.Add("class", "Hello");
var tagBuilder = new TagBuilder("p");
@ -839,7 +847,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
tagHelperOutput.Attributes.Add(originalName, "Hello");
var tagBuilder = new TagBuilder("p");
@ -860,7 +869,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var tagBuilder = new TagBuilder("p");
var expectedAttribute = new TagHelperAttribute("visible", "val < 3");
@ -881,7 +891,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var tagBuilder = new TagBuilder("p");
var expectedAttribute1 = new TagHelperAttribute("class", "btn");
@ -907,7 +918,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedAttribute = new TagHelperAttribute("class", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute);
@ -928,7 +940,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperOutput = new TagHelperOutput(
tagName: "p",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
var expectedOutputAttribute = new TagHelperAttribute("class", "btn");
tagHelperOutput.Attributes.Add(expectedOutputAttribute);

View File

@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
expectedTagName,
htmlAttributes,
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");

View File

@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "id", "myvalidationmessage" }
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"span",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -168,7 +168,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"span",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml(childContent);
@ -227,7 +227,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
"span",
attributes: new TagHelperAttributeList(),
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent(childContent);
@ -268,7 +268,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "span",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
{ "class", "form-control" }
},
getChildContentAsync: useCachedResult =>
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
@ -111,7 +111,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
@ -165,7 +166,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent("Content of validation summary");
@ -213,7 +215,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
@ -268,7 +271,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var output = new TagHelperOutput(
tagName: "div",
attributes: new TagHelperAttributeList(),
getChildContentAsync: (_) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()));
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent("Content of validation message");

View File

@ -0,0 +1,44 @@
// 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 Microsoft.AspNet.Mvc;
namespace TagHelpersWebSite.Controllers
{
public class EncodersController : Controller
{
public IActionResult Index()
{
ViewData["Title"] = "Using the default HTML encoder";
return View();
}
public IActionResult CustomEncoder()
{
ViewData["Title"] = "Using a custom HTML encoder";
return View();
}
public IActionResult NullEncoder()
{
ViewData["Title"] = "Using the null HTML encoder";
return View();
}
// We've defined the behavior when multiple tag helpers target the same element. But this is an extreme corner
// case since one tag helper even using anything but the default HTML encoder is not going to be common.
public IActionResult TwoEncoders()
{
ViewData["Title"] = "Using two HTML encoders";
return View();
}
// We've defined the behavior when multiple tag helpers target the same element. But this is an extreme corner
// case since one tag helper even using anything but the default HTML encoder is not going to be common.
public IActionResult ThreeEncoders()
{
ViewData["Title"] = "Using three HTML encoders";
return View();
}
}
}

View File

@ -0,0 +1,81 @@
// 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.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
namespace TagHelpersWebSite.TagHelpers
{
[HtmlTargetElement("pre")]
public class CustomEncoderTagHelper : TagHelper
{
public override int Order => 1;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var encoder = new CustomEncoder();
var customContent = await output.GetChildContentAsync(encoder);
// Note this is very unsafe. Should always post-process content that may not be fully HTML encoded before
// writing it into a response. Here for example, could pass SetContent() a string and that would be
// HTML encoded later.
output.PreContent
.SetHtmlContent("Custom encoder: ")
.AppendHtml(customContent)
.AppendHtml("<br />");
}
// Simple encoder that just wraps "string" as "Custom[[string]]". Note: Lacks all parameter checks.
private class CustomEncoder : HtmlEncoder
{
public CustomEncoder()
{
}
public override int MaxOutputCharactersPerInputCharacter => 1;
public override string Encode(string value) => $"Custom[[{ value }]]";
public override void Encode(TextWriter output, char[] value, int startIndex, int characterCount)
{
if (characterCount == 0)
{
return;
}
output.Write("Custom[[");
output.Write(value, startIndex, characterCount);
output.Write("]]");
}
public override void Encode(TextWriter output, string value, int startIndex, int characterCount)
{
if (characterCount == 0)
{
return;
}
output.Write("Custom[[");
output.Write(value.Substring(startIndex, characterCount));
output.Write("]]");
}
public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) => -1;
public override unsafe bool TryEncodeUnicodeScalar(
int unicodeScalar,
char* buffer,
int bufferLength,
out int numberOfCharactersWritten)
{
numberOfCharactersWritten = 0;
return false;
}
public override bool WillEncode(int unicodeScalar) => false;
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
namespace TagHelpersWebSite.TagHelpers
{
[HtmlTargetElement("pre")]
[HtmlTargetElement("inner")]
[OutputElementHint("pre")]
public class DefaultEncoderTagHelper : TagHelper
{
public override int Order => 2;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var defaultContent = await output.GetChildContentAsync();
output.Content
.SetHtmlContent("Default encoder: ")
.AppendHtml(defaultContent);
output.TagName = "pre";
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;
namespace TagHelpersWebSite.TagHelpers
{
[HtmlTargetElement("pre")]
public class NullEncoderTagHelper : TagHelper
{
public override int Order => 3;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var nullContent = await output.GetChildContentAsync(NullHtmlEncoder.Default);
// Note this is very unsafe. Should always post-process content that may not be fully HTML encoded before
// writing it into a response. Here for example, could pass SetContent() a string and that would be
// HTML encoded later.
output.PostContent
.SetHtmlContent("<br />Null encoder: ")
.AppendHtml(nullContent);
}
}
}

View File

@ -0,0 +1,9 @@
@addTagHelper TagHelpersWebSite.TagHelpers.CustomEncoderTagHelper, TagHelpersWebSite
@{
var count = 0;
var innerCount = 0;
}
<pre>@($"Outer knows <b>{ ++count } < 4</b>")
<inner>@($"Inner knows <b>{ ++innerCount } < 4</b>")</inner></pre>

View File

@ -0,0 +1,9 @@
@addTagHelper TagHelpersWebSite.TagHelpers.DefaultEncoderTagHelper, TagHelpersWebSite
@{
var count = 0;
var innerCount = 0;
}
<pre>@($"Outer knows <b>{ ++count } < 4</b>")
<inner>@($"Inner knows <b>{ ++innerCount } < 4</b>")</inner></pre>

View File

@ -0,0 +1,9 @@
@addTagHelper TagHelpersWebSite.TagHelpers.NullEncoderTagHelper, TagHelpersWebSite
@{
var count = 0;
var innerCount = 0;
}
<pre>@($"Outer knows <b>{ ++count } < 4</b>")
<inner>@($"Inner knows <b>{ ++innerCount } < 4</b>")</inner></pre>

View File

@ -0,0 +1,11 @@
@addTagHelper TagHelpersWebSite.TagHelpers.CustomEncoderTagHelper, TagHelpersWebSite
@addTagHelper TagHelpersWebSite.TagHelpers.DefaultEncoderTagHelper, TagHelpersWebSite
@addTagHelper TagHelpersWebSite.TagHelpers.NullEncoderTagHelper, TagHelpersWebSite
@{
var count = 0;
var innerCount = 0;
}
<pre>@($"Outer knows <b>{ ++count } < 4</b>")
<inner>@($"Inner knows <b>{ ++innerCount } < 4</b>")</inner></pre>

View File

@ -0,0 +1,10 @@
@addTagHelper TagHelpersWebSite.TagHelpers.CustomEncoderTagHelper, TagHelpersWebSite
@addTagHelper TagHelpersWebSite.TagHelpers.NullEncoderTagHelper, TagHelpersWebSite
@{
var count = 0;
var innerCount = 0;
}
<pre>@($"Outer knows <b>{ ++count } < 4</b>")
<inner>@($"Inner knows <b>{ ++innerCount } < 4</b>")</inner></pre>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>@ViewData["Title"] - My MVC 6 Application</title>
</head>
<body>
<h1>MVC 6 - @ViewData["Title"]</h1>
<div>
@RenderBody()
</div>
</body>
</html>

View File

@ -0,0 +1 @@
@removeTagHelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite"

View File

@ -0,0 +1,3 @@
@{
Layout = "_Layout.cshtml";
}

View File

@ -1,6 +1,8 @@
{
"compilationOptions": {
"emitEntryPoint": true
"allowUnsafe": true,
"emitEntryPoint": true,
"warningsAsErrors": true
},
"commands": {
"web": "TagHelpersWebSite"

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />
</handlers>
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" forwardWindowsAuthToken="false" startupTimeLimit="3600" />
</system.webServer>
</configuration>