// 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.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
///
/// Executes an .
///
public class ViewExecutor
{
///
/// The default content-type header value for views, text/html; charset=utf-8.
///
public static readonly string DefaultContentType = "text/html; charset=utf-8";
///
/// Creates a new .
///
/// The .
/// The .
/// The .
/// The .
/// The .
public ViewExecutor(
IOptions viewOptions,
IHttpResponseStreamWriterFactory writerFactory,
ICompositeViewEngine viewEngine,
ITempDataDictionaryFactory tempDataFactory,
DiagnosticSource diagnosticSource)
{
if (viewOptions == null)
{
throw new ArgumentNullException(nameof(viewOptions));
}
if (writerFactory == null)
{
throw new ArgumentNullException(nameof(writerFactory));
}
if (viewEngine == null)
{
throw new ArgumentNullException(nameof(viewEngine));
}
if (tempDataFactory == null)
{
throw new ArgumentNullException(nameof(tempDataFactory));
}
if (diagnosticSource == null)
{
throw new ArgumentNullException(nameof(diagnosticSource));
}
ViewOptions = viewOptions.Value;
WriterFactory = writerFactory;
ViewEngine = viewEngine;
TempDataFactory = tempDataFactory;
DiagnosticSource = diagnosticSource;
}
///
/// Gets the .
///
protected DiagnosticSource DiagnosticSource { get; }
///
/// Gets the .
///
protected ITempDataDictionaryFactory TempDataFactory { get; }
///
/// Gets the default .
///
protected IViewEngine ViewEngine { get; }
///
/// Gets the .
///
protected MvcViewOptions ViewOptions { get; }
///
/// Gets the .
///
protected IHttpResponseStreamWriterFactory WriterFactory { get; }
///
/// Executes a view asynchronously.
///
/// The associated with the current request.
/// The .
/// The .
/// The .
///
/// The content-type header value to set in the response. If null,
/// will be used.
///
///
/// The HTTP status code to set in the response. May be null.
///
/// A which will complete when view execution is completed.
public virtual async Task ExecuteAsync(
ActionContext actionContext,
IView view,
ViewDataDictionary viewData,
ITempDataDictionary tempData,
string contentType,
int? statusCode)
{
if (actionContext == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
if (view == null)
{
throw new ArgumentNullException(nameof(view));
}
if (viewData == null)
{
var services = actionContext.HttpContext.RequestServices;
var metadataProvider = services.GetRequiredService();
viewData = new ViewDataDictionary(metadataProvider, actionContext.ModelState);
}
if (tempData == null)
{
tempData = TempDataFactory.GetTempData(actionContext.HttpContext);
}
var response = actionContext.HttpContext.Response;
string resolvedContentType = null;
Encoding resolvedContentTypeEncoding = null;
ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
contentType,
response.ContentType,
DefaultContentType,
out resolvedContentType,
out resolvedContentTypeEncoding);
response.ContentType = resolvedContentType;
if (statusCode != null)
{
response.StatusCode = statusCode.Value;
}
using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
var viewContext = new ViewContext(
actionContext,
view,
viewData,
tempData,
writer,
ViewOptions.HtmlHelperOptions);
DiagnosticSource.BeforeView(view, viewContext);
await view.RenderAsync(viewContext);
DiagnosticSource.AfterView(view, viewContext);
// Perf: Invoke FlushAsync to ensure any buffered content is asynchronously written to the underlying
// response asynchronously. In the absence of this line, the buffer gets synchronously written to the
// response as part of the Dispose which has a perf impact.
await writer.FlushAsync();
}
}
}
}