parent
a2023d35ee
commit
5d32d224f4
|
|
@ -6,8 +6,24 @@ using Microsoft.AspNet.Razor;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contracts for a Razor host that parses Razor files and generates C# code.
|
||||
/// </summary>
|
||||
public interface IMvcRazorHost
|
||||
{
|
||||
/// <summary>
|
||||
/// Flag that indicates if page execution instrumentation code should be injected into the output.
|
||||
/// </summary>
|
||||
bool EnableInstrumentation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses and generates the contents of a Razor file represented by <paramref name="inputStream"/>.
|
||||
/// </summary>
|
||||
/// <param name="rootRelativePath">The path of the relative to the root of the application.
|
||||
/// Used to generate line pragmas and calculate the class name of the generated type.</param>
|
||||
/// <param name="inputStream">A <see cref="Stream"/> that represents the Razor contents.</param>
|
||||
/// <returns>A <see cref="GeneratorResults"/> instance that represents the results of code generation.
|
||||
/// </returns>
|
||||
GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
templateTypeName: "Microsoft.AspNet.Mvc.Razor.HelperResult",
|
||||
defineSectionMethodName: "DefineSection")
|
||||
{
|
||||
ResolveUrlMethodName = "Href"
|
||||
ResolveUrlMethodName = "Href",
|
||||
BeginContextMethodName = "BeginContext",
|
||||
EndContextMethodName = "EndContext"
|
||||
};
|
||||
|
||||
foreach (var ns in _defaultNamespaces)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
|
@ -44,6 +45,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
string Layout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a <see cref="IPageExecutionContext"/> instance used to instrument the page execution.
|
||||
/// </summary>
|
||||
IPageExecutionContext PageExecutionContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sections that can be rendered by this page.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("FlushPointCannotBeInvoked"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The {0} returned by '{1}' must be an instance of '{2}'.
|
||||
/// </summary>
|
||||
internal static string Instrumentation_WriterMustBeBufferedTextWriter
|
||||
{
|
||||
get { return GetString("Instrumentation_WriterMustBeBufferedTextWriter"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The {0} returned by '{1}' must be an instance of '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatInstrumentation_WriterMustBeBufferedTextWriter(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Instrumentation_WriterMustBeBufferedTextWriter"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layout view '{0}' could not be located.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using System.Security.Principal;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
|
|
@ -47,8 +48,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <inheritdoc />
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Layout { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPageExecutionContext PageExecutionContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TextWriter that the page is writing output to.
|
||||
/// </summary>
|
||||
|
|
@ -268,6 +273,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
// Calculate length of the source span by the position of the next value (or suffix)
|
||||
var sourceLength = next.Position - attrVal.Value.Position;
|
||||
|
||||
BeginContext(attrVal.Value.Position, sourceLength, isLiteral: attrVal.Literal);
|
||||
// The extra branching here is to ensure that we call the Write*To(string) overload whe
|
||||
// possible.
|
||||
if (attrVal.Literal && stringValue != null)
|
||||
|
|
@ -287,6 +293,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
WriteTo(writer, val.Value);
|
||||
}
|
||||
|
||||
EndContext();
|
||||
wroteSomething = true;
|
||||
}
|
||||
if (wroteSomething)
|
||||
|
|
@ -308,7 +315,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
private void WritePositionTaggedLiteral(TextWriter writer, string value, int position)
|
||||
{
|
||||
BeginContext(position, value.Length, isLiteral: true);
|
||||
WriteLiteralTo(writer, value);
|
||||
EndContext();
|
||||
}
|
||||
|
||||
private void WritePositionTaggedLiteral(TextWriter writer, PositionTagged<string> value)
|
||||
|
|
@ -420,6 +429,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
}
|
||||
|
||||
public void BeginContext(int position, int length, bool isLiteral)
|
||||
{
|
||||
PageExecutionContext?.BeginContext(position, length, isLiteral);
|
||||
}
|
||||
|
||||
public void EndContext()
|
||||
{
|
||||
PageExecutionContext?.EndContext();
|
||||
}
|
||||
|
||||
private void EnsureMethodCanBeInvoked(string methodName)
|
||||
{
|
||||
if (PreviousSectionWriters == null)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// 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 RazorTextWriter : TextWriter
|
||||
public class RazorTextWriter : TextWriter, IBufferedTextWriter
|
||||
{
|
||||
private static readonly Task _completedTask = Task.FromResult(0);
|
||||
private readonly TextWriter _unbufferedWriter;
|
||||
|
|
@ -43,9 +43,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
get { return _encoding; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag that determines if this instance of <see cref="RazorTextWriter"/> is buffering content.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public bool IsBuffering { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -267,10 +265,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
await _unbufferedWriter.FlushAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the content of the <see cref="RazorTextWriter"/> to the <see cref="TextWriter"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to copy contents to.</param>
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(TextWriter writer)
|
||||
{
|
||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||
|
|
@ -286,11 +281,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the content of the <see cref="RazorTextWriter"/> to the specified <see cref="TextWriter"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to copy contents to.</param>
|
||||
/// <returns>A task that represents the asynchronous copy operation.</returns>
|
||||
/// <inheritdoc />
|
||||
public Task CopyToAsync(TextWriter writer)
|
||||
{
|
||||
var targetRazorTextWriter = writer as RazorTextWriter;
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@
|
|||
<data name="FlushPointCannotBeInvoked" xml:space="preserve">
|
||||
<value>'{0}' cannot be invoked when a Layout page is set to be executed.</value>
|
||||
</data>
|
||||
<data name="Instrumentation_WriterMustBeBufferedTextWriter" xml:space="preserve">
|
||||
<value>The {0} returned by '{1}' must be an instance of '{2}'.</value>
|
||||
</data>
|
||||
<data name="LayoutCannotBeLocated" xml:space="preserve">
|
||||
<value>The layout view '{0}' could not be located.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contracts for a <see cref="TextWriter"/> that buffers its content.
|
||||
/// </summary>
|
||||
[AssemblyNeutral]
|
||||
public interface IBufferedTextWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a flag that determines if content is currently being buffered.
|
||||
/// </summary>
|
||||
bool IsBuffering { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffered content to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to copy the contents to./param>
|
||||
void CopyTo(TextWriter writer);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously copies the buffered content to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to copy the contents to./param>
|
||||
/// <returns>A <see cref="Task"/> representing the copy operation.</returns>
|
||||
Task CopyToAsync(TextWriter writer);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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 Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.PageExecutionInstrumentation
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contracts for a execution context that instruments web page execution.
|
||||
/// </summary>
|
||||
[AssemblyNeutral]
|
||||
public interface IPageExecutionContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked at the start of a write operation.
|
||||
/// </summary>
|
||||
/// <param name="position">The absolute character position of the expression or text in the Razor file.</param>
|
||||
/// <param name="length">The character length of the expression or text in the Razor file.</param>
|
||||
/// <param name="isLiteral">A flag that indicates if the operation is for a literal text and not for a
|
||||
/// language expression.</param>
|
||||
void BeginContext(int position, int length, bool isLiteral);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked at the end of a write operation.
|
||||
/// </summary>
|
||||
void EndContext();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.PageExecutionInstrumentation
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contracts for a HTTP feature that provides the context to instrument a web page.
|
||||
/// </summary>
|
||||
[AssemblyNeutral]
|
||||
public interface IPageExecutionListenerFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorates the <see cref="TextWriter"/> used by web page instances to
|
||||
/// write the result to.
|
||||
/// </summary>
|
||||
/// <param name="writer">The output <see cref="TextWriter"/> for the web page.</param>
|
||||
/// <returns>A <see cref="TextWriter"/> that wraps <paramref name="writer"/>.</returns>
|
||||
/// <remarks>
|
||||
TextWriter DecorateWriter(TextWriter writer);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IPageExecutionContext"/> for the specified <paramref name="sourceFilePath"/>.
|
||||
/// </summary>
|
||||
/// <param name="sourceFilePath">The path of the <see cref="Mvc.Razor.IRazorPage"/>.</param>
|
||||
/// <param name="writer">The <see cref="TextWriter"/> obtained from <see cref="DecorateWriter(TextWriter)"/>.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IPageExecutionContext GetContext(string sourceFilePath, TextWriter writer);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,10 @@
|
|||
// 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 Microsoft.AspNet.Razor;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Moq;
|
||||
|
|
@ -351,6 +351,68 @@ Layout end
|
|||
Assert.DoesNotThrow(() => page.SectionWriters["test-section"].WriteTo(TextWriter.Null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAttribute_CallsBeginAndEndContext_OnPageExecutionListenerContext()
|
||||
{
|
||||
// Arrange
|
||||
var page = CreatePage(p =>
|
||||
{
|
||||
p.WriteAttribute("href",
|
||||
new PositionTagged<string>("prefix", 0),
|
||||
new PositionTagged<string>("suffix", 34),
|
||||
new AttributeValue(new PositionTagged<string>("prefix", 0),
|
||||
new PositionTagged<object>("attr1-value", 8),
|
||||
literal: true),
|
||||
new AttributeValue(new PositionTagged<string>("prefix2", 22),
|
||||
new PositionTagged<object>("attr2", 29),
|
||||
literal: false));
|
||||
});
|
||||
var context = new Mock<IPageExecutionContext>(MockBehavior.Strict);
|
||||
var sequence = new MockSequence();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(0, 6, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(8, 14, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(22, 7, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(29, 5, false)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(34, 6, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
page.PageExecutionContext = context.Object;
|
||||
|
||||
// Act
|
||||
await page.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
context.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAttribute_CallsBeginAndEndContext_OnPrefixAndSuffixValues()
|
||||
{
|
||||
// Arrange
|
||||
var page = CreatePage(p =>
|
||||
{
|
||||
p.WriteAttribute("href",
|
||||
new PositionTagged<string>("prefix", 0),
|
||||
new PositionTagged<string>("tail", 7));
|
||||
});
|
||||
var context = new Mock<IPageExecutionContext>(MockBehavior.Strict);
|
||||
var sequence = new MockSequence();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(0, 6, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.BeginContext(7, 4, true)).Verifiable();
|
||||
context.InSequence(sequence).Setup(f => f.EndContext()).Verifiable();
|
||||
page.PageExecutionContext = context.Object;
|
||||
|
||||
// Act
|
||||
await page.ExecuteAsync();
|
||||
|
||||
// Assert
|
||||
context.Verify();
|
||||
}
|
||||
|
||||
private static TestableRazorPage CreatePage(Action<TestableRazorPage> executeAction,
|
||||
ViewContext context = null)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue