// 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.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
///
/// Finds and executes an for a .
///
public class ViewResultExecutor : ViewExecutor, IActionResultExecutor
{
private const string ActionNameKey = "action";
///
/// Creates a new .
///
/// The .
/// The .
/// The .
/// The .
/// The .
/// The .
/// The .
public ViewResultExecutor(
IOptions viewOptions,
IHttpResponseStreamWriterFactory writerFactory,
ICompositeViewEngine viewEngine,
ITempDataDictionaryFactory tempDataFactory,
DiagnosticSource diagnosticSource,
ILoggerFactory loggerFactory,
IModelMetadataProvider modelMetadataProvider)
: base(viewOptions, writerFactory, viewEngine, tempDataFactory, diagnosticSource, modelMetadataProvider)
{
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
Logger = loggerFactory.CreateLogger();
}
///
/// Gets the .
///
protected ILogger Logger { get; }
///
/// Attempts to find the associated with .
///
/// The associated with the current request.
/// The .
/// A .
public virtual ViewEngineResult FindView(ActionContext actionContext, ViewResult viewResult)
{
if (actionContext == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
if (viewResult == null)
{
throw new ArgumentNullException(nameof(viewResult));
}
var viewEngine = viewResult.ViewEngine ?? ViewEngine;
var viewName = viewResult.ViewName ?? GetActionName(actionContext);
var stopwatch = ValueStopwatch.StartNew();
var result = viewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
var originalResult = result;
if (!result.Success)
{
result = viewEngine.FindView(actionContext, viewName, isMainPage: true);
}
Logger.ViewResultExecuting(result.ViewName);
if (!result.Success)
{
if (originalResult.SearchedLocations.Any())
{
if (result.SearchedLocations.Any())
{
// Return a new ViewEngineResult listing all searched locations.
var locations = new List(originalResult.SearchedLocations);
locations.AddRange(result.SearchedLocations);
result = ViewEngineResult.NotFound(viewName, locations);
}
else
{
// GetView() searched locations but FindView() did not. Use first ViewEngineResult.
result = originalResult;
}
}
}
if (result.Success)
{
if (DiagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.ViewFound"))
{
DiagnosticSource.Write(
"Microsoft.AspNetCore.Mvc.ViewFound",
new
{
actionContext = actionContext,
isMainPage = true,
result = viewResult,
viewName = viewName,
view = result.View,
});
}
Logger.ViewFound(result.View, stopwatch.GetElapsedTime());
}
else
{
if (DiagnosticSource.IsEnabled("Microsoft.AspNetCore.Mvc.ViewNotFound"))
{
DiagnosticSource.Write(
"Microsoft.AspNetCore.Mvc.ViewNotFound",
new
{
actionContext = actionContext,
isMainPage = true,
result = viewResult,
viewName = viewName,
searchedLocations = result.SearchedLocations
});
}
Logger.ViewNotFound(viewName, result.SearchedLocations);
}
return result;
}
///
public async Task ExecuteAsync(ActionContext context, ViewResult result)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
var stopwatch = ValueStopwatch.StartNew();
var viewEngineResult = FindView(context, result);
viewEngineResult.EnsureSuccessful(originalLocations: null);
var view = viewEngineResult.View;
using (view as IDisposable)
{
await ExecuteAsync(
context,
view,
result.ViewData,
result.TempData,
result.ContentType,
result.StatusCode);
}
Logger.ViewResultExecuted(viewEngineResult.ViewName, stopwatch.GetElapsedTime());
}
private static string GetActionName(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!context.RouteData.Values.TryGetValue(ActionNameKey, out var routeValue))
{
return null;
}
var actionDescriptor = context.ActionDescriptor;
string normalizedValue = null;
if (actionDescriptor.RouteValues.TryGetValue(ActionNameKey, out var value) &&
!string.IsNullOrEmpty(value))
{
normalizedValue = value;
}
var stringRouteValue = routeValue?.ToString();
if (string.Equals(normalizedValue, stringRouteValue, StringComparison.OrdinalIgnoreCase))
{
return normalizedValue;
}
return stringRouteValue;
}
}
}