// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc.Razor
{
/// <summary>
/// Represents properties and methods that are needed in order to render a view that uses Razor syntax.
/// </summary>
public abstract class RazorPage : RazorPageBase
{
private readonly HashSet<string> _renderedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private bool _renderedBody;
private bool _ignoreBody;
private HashSet<string> _ignoredSections;
/// <summary>
/// An <see cref="HttpContext"/> representing the current request execution.
/// </summary>
public HttpContext Context => ViewContext?.HttpContext;
/// <summary>
/// In a Razor layout page, renders the portion of a content page that is not within a named section.
/// </summary>
/// <returns>The HTML content to render.</returns>
protected virtual IHtmlContent RenderBody()
{
if (BodyContent == null)
{
var message = Resources.FormatRazorPage_MethodCannotBeCalled(nameof(RenderBody), Path);
throw new InvalidOperationException(message);
}
_renderedBody = true;
return BodyContent;
}
/// <summary>
/// In a Razor layout page, ignores rendering the portion of a content page that is not within a named section.
/// </summary>
public void IgnoreBody()
{
_ignoreBody = true;
}
/// <summary>
/// Creates a named content section in the page that can be invoked in a Layout page using
/// <see cref="RenderSection(string)"/> or <see cref="RenderSectionAsync(string, bool)"/>.
/// </summary>
/// <param name="name">The name of the section to create.</param>
/// <param name="section">The <see cref="RenderAsyncDelegate"/> to execute when rendering the section.</param>
public override void DefineSection(string name, RenderAsyncDelegate section)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (section == null)
{
throw new ArgumentNullException(nameof(section));
}
if (SectionWriters.ContainsKey(name))
{
throw new InvalidOperationException(Resources.FormatSectionAlreadyDefined(name));
}
SectionWriters[name] = section;
}
/// <summary>
/// Returns a value that indicates whether the specified section is defined in the content page.
/// </summary>
/// <param name="name">The section name to search for.</param>
/// <returns><c>true</c> if the specified section is defined in the content page; otherwise, <c>false</c>.</returns>
public bool IsSectionDefined(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
EnsureMethodCanBeInvoked(nameof(IsSectionDefined));
return PreviousSectionWriters.ContainsKey(name);
}
/// <summary>
/// In layout pages, renders the content of the section named <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of the section to render.</param>
/// <returns>An empty <see cref="IHtmlContent"/>.</returns>
/// <remarks>The method writes to the <see cref="RazorPageBase.Output"/> and the value returned is a token
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
/// value does not represent the rendered content.</remarks>
public HtmlString RenderSection(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return RenderSection(name, required: true);
}
/// <summary>
/// In layout pages, renders the content of the section named <paramref name="name"/>.
/// </summary>
/// <param name="name">The section to render.</param>
/// <param name="required">Indicates if this section must be rendered.</param>
/// <returns>An empty <see cref="IHtmlContent"/>.</returns>
/// <remarks>The method writes to the <see cref="RazorPageBase.Output"/> and the value returned is a token
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
/// value does not represent the rendered content.</remarks>
public HtmlString RenderSection(string name, bool required)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
EnsureMethodCanBeInvoked(nameof(RenderSection));
var task = RenderSectionAsyncCore(name, required);
return task.GetAwaiter().GetResult();
}
/// <summary>
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
/// </summary>
/// <param name="name">The section to render.</param>
/// <returns>
/// A <see cref="Task{HtmlString}"/> that on completion returns an empty <see cref="IHtmlContent"/>.
/// </returns>
/// <remarks>The method writes to the <see cref="RazorPageBase.Output"/> and the value returned is a token
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
/// value does not represent the rendered content.</remarks>
public Task<HtmlString> RenderSectionAsync(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return RenderSectionAsync(name, required: true);
}
/// <summary>
/// In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
/// </summary>
/// <param name="name">The section to render.</param>
/// <param name="required">Indicates the <paramref name="name"/> section must be registered
/// (using <c>@section</c>) in the page.</param>
/// <returns>
/// A <see cref="Task{HtmlString}"/> that on completion returns an empty <see cref="IHtmlContent"/>.
/// </returns>
/// <remarks>The method writes to the <see cref="RazorPageBase.Output"/> and the value returned is a token
/// value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
/// value does not represent the rendered content.</remarks>
/// <exception cref="InvalidOperationException">if <paramref name="required"/> is <c>true</c> and the section
/// was not registered using the <c>@section</c> in the Razor page.</exception>
public Task<HtmlString> RenderSectionAsync(string name, bool required)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
EnsureMethodCanBeInvoked(nameof(RenderSectionAsync));
return RenderSectionAsyncCore(name, required);
}
private async Task<HtmlString> RenderSectionAsyncCore(string sectionName, bool required)
{
if (_renderedSections.Contains(sectionName))
{
var message = Resources.FormatSectionAlreadyRendered(nameof(RenderSectionAsync), Path, sectionName);
throw new InvalidOperationException(message);
}
if (PreviousSectionWriters.TryGetValue(sectionName, out var renderDelegate))
{
_renderedSections.Add(sectionName);
await renderDelegate();
// Return a token value that allows the Write call that wraps the RenderSection \ RenderSectionAsync
// to succeed.
return HtmlString.Empty;
}
else if (required)
{
// If the section is not found, and it is not optional, throw an error.
var viewContext = ViewContext;
throw new InvalidOperationException(
Resources.FormatSectionNotDefined(
viewContext.ExecutingFilePath,
sectionName,
viewContext.View.Path));
}
else
{
// If the section is optional and not found, then don't do anything.
return null;
}
}
/// <summary>
/// In layout pages, ignores rendering the content of the section named <paramref name="sectionName"/>.
/// </summary>
/// <param name="sectionName">The section to ignore.</param>
public void IgnoreSection(string sectionName)
{
if (sectionName == null)
{
throw new ArgumentNullException(nameof(sectionName));
}
if (!PreviousSectionWriters.ContainsKey(sectionName))
{
// If the section is not defined, throw an error.
throw new InvalidOperationException(Resources.FormatSectionNotDefined(
ViewContext.ExecutingFilePath,
sectionName,
ViewContext.View.Path));
}
if (_ignoredSections == null)
{
_ignoredSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}
_ignoredSections.Add(sectionName);
}
/// <inheritdoc />
public override void EnsureRenderedBodyOrSections()
{
// a) all sections defined for this page are rendered.
// b) if no sections are defined, then the body is rendered if it's available.
if (PreviousSectionWriters != null && PreviousSectionWriters.Count > 0)
{
var sectionsNotRendered = PreviousSectionWriters.Keys.Except(
_renderedSections,
StringComparer.OrdinalIgnoreCase);
string[] sectionsNotIgnored;
if (_ignoredSections != null)
{
sectionsNotIgnored = sectionsNotRendered.Except(_ignoredSections, StringComparer.OrdinalIgnoreCase).ToArray();
}
else
{
sectionsNotIgnored = sectionsNotRendered.ToArray();
}
if (sectionsNotIgnored.Length > 0)
{
var sectionNames = string.Join(", ", sectionsNotIgnored);
throw new InvalidOperationException(Resources.FormatSectionsNotRendered(Path, sectionNames, nameof(IgnoreSection)));
}
}
else if (BodyContent != null && !_renderedBody && !_ignoreBody)
{
// There are no sections defined, but RenderBody was NOT called.
// If a body was defined and the body not ignored, then RenderBody should have been called.
var message = Resources.FormatRenderBodyNotCalled(nameof(RenderBody), Path, nameof(IgnoreBody));
throw new InvalidOperationException(message);
}
}
public override void BeginContext(int position, int length, bool isLiteral)
{
// noop
}
public override void EndContext()
{
// noop
}
private void EnsureMethodCanBeInvoked(string methodName)
{
if (PreviousSectionWriters == null)
{
throw new InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName, Path));
}
}
}
}
VB:
Option Compare Text
Option Explicit On
Option Infer Off
Option Strict On
' 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.
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Html
Imports Microsoft.AspNetCore.Http
Namespace Microsoft.AspNetCore.Mvc.Razor
''' <summary>
''' Represents properties and methods that are needed in order to render a view that uses Razor syntax.
''' </summary>
Public MustInherit Class RazorPage
Inherits RazorPageBase
Private ReadOnly _renderedSections As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
Private _renderedBody As Boolean
Private _ignoreBody As Boolean
Private _ignoredSections As HashSet(Of String)
''' <summary>
''' An <see cref= "HttpContext"/> representing the current request execution.
''' </summary>
Public ReadOnly Property Context As HttpContext
Get
Return ViewContext?.HttpContext
End Get
End Property
''' <summary>
''' In a Razor layout page, renders the portion of a content page that is not within a named section.
''' </summary>
''' <returns>The HTML content to render.</returns>
Protected Overridable Function RenderBody() As IHtmlContent
If BodyContent Is Nothing Then
Dim message = Resources.FormatRazorPage_MethodCannotBeCalled(NameOf(RenderBody), Path)
Throw New InvalidOperationException(message)
End If
_renderedBody = True
Return BodyContent
End Function
''' <summary>
''' In a Razor layout page, ignores rendering the portion of a content page that is not within a named section.
''' </summary>
Public Sub IgnoreBody()
_ignoreBody = True
End Sub
''' <summary>
''' Creates a named content section in the page that can be invoked in a Layout page using
''' <see cref= "RenderSection(String)"/> or <see cref= "RenderSectionAsync(String,Boolean)"/>.
''' </summary>
''' <param name="name">The name of the section to create.</param>
''' <param name="section">The <see cref= "RenderAsyncDelegate"/> to execute when rendering the section.</param>
Public Overrides Sub DefineSection(name As String, section As RenderAsyncDelegate)
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
If section Is Nothing Then
Throw New ArgumentNullException(NameOf(section))
End If
If SectionWriters.ContainsKey(name) Then
Throw New InvalidOperationException(Resources.FormatSectionAlreadyDefined(name))
End If
SectionWriters(name) = section
End Sub
''' <summary>
''' Returns a value that indicates whether the specified section is defined in the content page.
''' </summary>
''' <param name="name">The section name to search for.</param>
''' <returns><c>true</c> if the specified section is defined in the content page; otherwise, <c>false</c>.</returns>
Public Function IsSectionDefined(name As String) As Boolean
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
EnsureMethodCanBeInvoked(NameOf(IsSectionDefined))
Return PreviousSectionWriters.ContainsKey(name)
End Function
''' <summary>
''' In layout pages, renders the content of the section named <paramref name="name"/>.
''' </summary>
''' <param name="name">The name of the section to render.</param>
''' <returns>An empty <see cref= "IHtmlContent"/>.</returns>
''' <remarks>The method writes to the <see cref= "RazorPageBase.Output"/> and the value returned is a token
''' value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
''' value does not represent the rendered content.</remarks>
Public Function RenderSection(name As String) As HtmlString
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
Return RenderSection(name, required:=True)
End Function
''' <summary>
''' In layout pages, renders the content of the section named <paramref name="name"/>.
''' </summary>
''' <param name="name">The section to render.</param>
''' <param name="required">Indicates if this section must be rendered.</param>
''' <returns>An empty <see cref= "IHtmlContent"/>.</returns>
''' <remarks>The method writes to the <see cref= "RazorPageBase.Output"/> and the value returned is a token
''' value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
''' value does not represent the rendered content.</remarks>
Public Function RenderSection(name As String, required As Boolean) As HtmlString
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
EnsureMethodCanBeInvoked(NameOf(RenderSection))
Dim task As System.Threading.Tasks.Task(Of HtmlString) = RenderSectionAsyncCore(name, required)
Return task.GetAwaiter().GetResult()
End Function
''' <summary>
''' In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
''' </summary>
''' <param name="name">The section to render.</param>
''' <returns>
''' A <see cref= "Task(OfHtmlString)"/> that on completion returns an empty <see cref= "IHtmlContent"/>.
''' </returns>
''' <remarks>The method writes to the <see cref= "RazorPageBase.Output"/> and the value returned is a token
''' value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
''' value does not represent the rendered content.</remarks>
Public Function RenderSectionAsync(name As String) As Task_Renamed(Of HtmlString)
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
Return RenderSectionAsync(name, required:=True)
End Function
''' <summary>
''' In layout pages, asynchronously renders the content of the section named <paramref name="name"/>.
''' </summary>
''' <param name="name">The section to render.</param>
''' <param name="required">Indicates the <paramref name="name"/> section must be registered
''' (using <c>@section</c>) in the page.</param>
''' <returns>
''' A <see cref= "Task(OfHtmlString)"/> that on completion returns an empty <see cref= "IHtmlContent"/>.
''' </returns>
''' <remarks>The method writes to the <see cref= "RazorPageBase.Output"/> and the value returned is a token
''' value that allows the Write (produced due to @RenderSection(..)) to succeed. However the
''' value does not represent the rendered content.</remarks>
''' <exception cref= "InvalidOperationException">if <paramref name="required"/> is <c>true</c> and the section
''' was not registered using the <c>@section</c> in the Razor page.</exception>
Public Function RenderSectionAsync(name As String, required As Boolean) As Task_Renamed(Of HtmlString)
If name Is Nothing Then
Throw New ArgumentNullException(NameOf(name))
End If
EnsureMethodCanBeInvoked(NameOf(RenderSectionAsync))
Return RenderSectionAsyncCore(name, required)
End Function
Private Async Function RenderSectionAsyncCore(sectionName As String, required As Boolean) As Task_Renamed(Of HtmlString)
If _renderedSections.Contains(sectionName) Then
Dim message = Resources.FormatSectionAlreadyRendered(NameOf(RenderSectionAsync), Path, sectionName)
Throw New InvalidOperationException(message)
End If
Dim renderDelegate As String = Nothing
If PreviousSectionWriters.TryGetValue(sectionName, renderDelegate) Then
_renderedSections.Add(sectionName)
Await renderDelegate()
' Return a token value that allows the Write call that wraps the RenderSection \ RenderSectionAsync
' to succeed.
Return HtmlString.Empty
ElseIf required Then
' If the section is not found, and it is not optional, throw an error.
Dim _viewContext = ViewContext
Throw New InvalidOperationException( _
Resources.FormatSectionNotDefined( _
_viewContext.ExecutingFilePath,
sectionName,
_viewContext.View.Path))
Else
' If the section is optional and not found, then don't do anything.
Return Nothing
End If
End Function
''' <summary>
''' In layout pages, ignores rendering the content of the section named <paramref name="sectionName"/>.
''' </summary>
''' <param name="sectionName">The section to ignore.</param>
Public Sub IgnoreSection(sectionName As String)
If sectionName Is Nothing Then
Throw New ArgumentNullException(NameOf(sectionName))
End If
If Not PreviousSectionWriters.ContainsKey(sectionName) Then
' If the section is not defined, throw an error.
Throw New InvalidOperationException(Resources.FormatSectionNotDefined( _
ViewContext.ExecutingFilePath,
sectionName,
ViewContext.View.Path))
End If
If _ignoredSections Is Nothing Then
_ignoredSections = New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
End If
_ignoredSections.Add(sectionName)
End Sub
''' <inheritdoc/>
Public Overrides Sub EnsureRenderedBodyOrSections()
' a) all sections defined for this page are rendered.
' b) if no sections are defined, then the body is rendered if it's available.
If PreviousSectionWriters IsNot Nothing AndAlso PreviousSectionWriters.Count > 0 Then
Dim sectionsNotRendered = PreviousSectionWriters.Keys.Except( _
_renderedSections,
StringComparer.OrdinalIgnoreCase)
Dim sectionsNotIgnored As String()
If _ignoredSections IsNot Nothing Then
sectionsNotIgnored = sectionsNotRendered.Except(_ignoredSections, StringComparer.OrdinalIgnoreCase).ToArray()
Else
sectionsNotIgnored = sectionsNotRendered.ToArray()
End If
If sectionsNotIgnored.Length > 0 Then
Dim sectionNames As String = String.Join(", ", sectionsNotIgnored)
Throw New InvalidOperationException(Resources.FormatSectionsNotRendered(Path, sectionNames, NameOf(IgnoreSection)))
End If
ElseIf BodyContent IsNot Nothing AndAlso Not _renderedBody AndAlso Not _ignoreBody Then
' There are no sections defined, but RenderBody was NOT called.
' If a body was defined and the body not ignored, then RenderBody should have been called.
Dim message = Resources.FormatRenderBodyNotCalled(NameOf(RenderBody), Path, NameOf(IgnoreBody))
Throw New InvalidOperationException(message)
End If
End Sub
Public Overrides Sub BeginContext(position As Integer, _length As Integer, isLiteral As Boolean)
' noop
End Sub
Public Overrides Sub EndContext()
' noop
End Sub
Private Sub EnsureMethodCanBeInvoked(methodName As String)
If PreviousSectionWriters Is Nothing Then
Throw New InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName, Path))
End If
End Sub
End Class
End Namespace
Task_renamed Appeared 3 times, such as:
Public Function RenderSectionAsync(name As String) As Task_Renamed(Of HtmlString)
C#:
VB:
Task_renamed
Appeared 3 times, such as:Public Function RenderSectionAsync(name As String) As Task_Renamed(Of HtmlString)