// 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.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc.Razor
{
///
/// Represents properties and methods that are needed in order to render a view that uses Razor syntax.
///
public abstract class RazorPageBase : IRazorPage
{
private readonly Stack _textWriterStack = new Stack();
private StringWriter _valueBuffer;
private ITagHelperFactory _tagHelperFactory;
private IViewBufferScope _bufferScope;
private TextWriter _pageWriter;
private AttributeInfo _attributeInfo;
private TagHelperAttributeInfo _tagHelperAttributeInfo;
private IUrlHelper _urlHelper;
public virtual ViewContext ViewContext { get; set; }
public string Layout { get; set; }
///
/// Gets the that the page is writing output to.
///
///
/// Gets the that the page is writing output to.
///
public virtual TextWriter Output
{
get
{
if (ViewContext == null)
{
var message = Resources.FormatViewContextMustBeSet("ViewContext", "Output");
throw new InvalidOperationException(message);
}
return ViewContext.Writer;
}
}
///
public string Path { get; set; }
///
public IDictionary SectionWriters { get; } =
new Dictionary(StringComparer.OrdinalIgnoreCase);
///
/// Gets the dynamic view data dictionary.
///
public dynamic ViewBag => ViewContext?.ViewBag;
///
public bool IsLayoutBeingRendered { get; set; }
///
public IHtmlContent BodyContent { get; set; }
///
public IDictionary PreviousSectionWriters { get; set; }
///
/// Gets or sets a instance used to instrument the page execution.
///
[RazorInject]
public DiagnosticSource DiagnosticSource { get; set; }
///
/// Gets the to use when this
/// handles non- C# expressions.
///
[RazorInject]
public HtmlEncoder HtmlEncoder { get; set; }
///
/// Gets the of the current logged in user.
///
public virtual ClaimsPrincipal User => ViewContext?.HttpContext?.User;
///
/// Gets the from the .
///
/// Returns null if is null.
public ITempDataDictionary TempData => ViewContext?.TempData;
private Stack TagHelperScopes { get; } = new Stack();
private ITagHelperFactory TagHelperFactory
{
get
{
if (_tagHelperFactory == null)
{
var services = ViewContext.HttpContext.RequestServices;
_tagHelperFactory = services.GetRequiredService();
}
return _tagHelperFactory;
}
}
private IViewBufferScope BufferScope
{
get
{
if (_bufferScope == null)
{
var services = ViewContext.HttpContext.RequestServices;
_bufferScope = services.GetRequiredService();
}
return _bufferScope;
}
}
public abstract Task ExecuteAsync();
///
/// Format an error message about using an indexer when the tag helper property is null.
///
/// Name of the HTML attribute associated with the indexer.
/// Full name of the tag helper .
/// Dictionary property in the tag helper.
/// An error message about using an indexer when the tag helper property is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public string InvalidTagHelperIndexerAssignment(
string attributeName,
string tagHelperTypeName,
string propertyName)
{
return Resources.FormatRazorPage_InvalidTagHelperIndexerAssignment(
attributeName,
tagHelperTypeName,
propertyName);
}
///
/// Creates and activates a .
///
/// A type.
/// The activated .
///
/// must have a parameterless constructor.
///
public TTagHelper CreateTagHelper() where TTagHelper : ITagHelper
{
return TagHelperFactory.CreateTagHelper(ViewContext);
}
///
/// Starts a new writing scope and optionally overrides within that scope.
///
///
/// The to use when this handles
/// non- C# expressions. If null, does not change .
///
///
/// All writes to the or after calling this method will
/// be buffered until is called.
///
public void StartTagHelperWritingScope(HtmlEncoder encoder)
{
var buffer = new ViewBuffer(BufferScope, Path, ViewBuffer.TagHelperPageSize);
TagHelperScopes.Push(new TagHelperScopeInfo(buffer, HtmlEncoder, ViewContext.Writer));
// 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.
ViewContext.Writer = new ViewBufferTextWriter(buffer, ViewContext.Writer.Encoding);
}
///
/// Ends the current writing scope that was started by calling .
///
/// The buffered .
public TagHelperContent EndTagHelperWritingScope()
{
if (TagHelperScopes.Count == 0)
{
throw new InvalidOperationException(Resources.RazorPage_ThereIsNoActiveWritingScopeToEnd);
}
var scopeInfo = TagHelperScopes.Pop();
// Get the content written during the current scope.
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.AppendHtml(scopeInfo.Buffer);
// Restore previous scope.
HtmlEncoder = scopeInfo.HtmlEncoder;
ViewContext.Writer = scopeInfo.Writer;
return tagHelperContent;
}
///
/// Starts a new scope for writing attribute values.
///
///
/// All writes to the or after calling this method will
/// be buffered until is called.
/// The content will be buffered using a shared within this
/// Nesting of and method calls
/// is not supported.
///
public void BeginWriteTagHelperAttribute()
{
if (_pageWriter != null)
{
throw new InvalidOperationException(Resources.RazorPage_NestingAttributeWritingScopesNotSupported);
}
_pageWriter = ViewContext.Writer;
if (_valueBuffer == null)
{
_valueBuffer = new StringWriter();
}
// We need to replace the ViewContext's Writer to ensure that all content (including content written
// from HTML helpers) is redirected.
ViewContext.Writer = _valueBuffer;
}
///
/// Ends the current writing scope that was started by calling .
///
/// The content buffered by the shared of this .
///
/// This method assumes that there will be no nesting of
/// and method calls.
///
public string EndWriteTagHelperAttribute()
{
if (_pageWriter == null)
{
throw new InvalidOperationException(Resources.RazorPage_ThereIsNoActiveWritingScopeToEnd);
}
var content = _valueBuffer.ToString();
_valueBuffer.GetStringBuilder().Clear();
// Restore previous writer.
ViewContext.Writer = _pageWriter;
_pageWriter = null;
return content;
}
// Internal for unit testing.
protected internal virtual void PushWriter(TextWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_textWriterStack.Push(ViewContext.Writer);
ViewContext.Writer = writer;
}
// Internal for unit testing.
protected internal virtual TextWriter PopWriter()
{
ViewContext.Writer = _textWriterStack.Pop();
return ViewContext.Writer;
}
public virtual string Href(string contentPath)
{
if (contentPath == null)
{
throw new ArgumentNullException(nameof(contentPath));
}
if (_urlHelper == null)
{
var services = ViewContext?.HttpContext.RequestServices;
var factory = services.GetRequiredService();
_urlHelper = factory.GetUrlHelper(ViewContext);
}
return _urlHelper.Content(contentPath);
}
///
/// Creates a named content section in the page that can be invoked in a Layout page using
/// RenderSection or RenderSectionAsync
///
/// The name of the section to create.
/// The delegate to execute when rendering the section.
/// This is a temporary placeholder method to support ASP.NET Core 2.0.0 editor code generation.
[EditorBrowsable(EditorBrowsableState.Never)]
protected void DefineSection(string name, Func