using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Filters; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Controllers; /// /// Represents the default front-end rendering controller. /// [ModelBindingException] [PublishedRequestFilter] [MaintenanceModeActionFilter] public class RenderController : UmbracoPageController, IRenderController { private readonly ILogger _logger; private readonly IUmbracoContextAccessor _umbracoContextAccessor; /// /// Initializes a new instance of the class. /// public RenderController(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, compositeViewEngine) { _logger = logger; _umbracoContextAccessor = umbracoContextAccessor; } /// /// Gets the umbraco context /// protected IUmbracoContext UmbracoContext { get { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); return umbracoContext; } } /// /// The default action to render the front-end view. /// public virtual IActionResult Index() => CurrentTemplate(new ContentModel(CurrentPage)); /// /// Before the controller executes we will handle redirects and not founds /// public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { IPublishedRequest pcr = UmbracoRouteValues.PublishedRequest; if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) { _logger.LogDebug( "Response status: Content={Content}, StatusCode={ResponseStatusCode}, Culture={Culture}, Segment={Segment}", pcr.PublishedContent?.Id ?? -1, pcr.ResponseStatusCode, pcr.Culture, pcr.Segment); } UmbracoRouteResult routeStatus = pcr.GetRouteResult(); switch (routeStatus) { case UmbracoRouteResult.Redirect: // set the redirect result and do not call next to short circuit context.Result = pcr.IsRedirectPermanent() ? RedirectPermanent(pcr.RedirectUrl!) : Redirect(pcr.RedirectUrl!); break; case UmbracoRouteResult.NotFound: // set the redirect result and do not call next to short circuit context.Result = GetNoTemplateResult(pcr); break; case UmbracoRouteResult.Success: default: // Check if there's a ProxyViewDataFeature in the request. // If there it is means that we are proxying/executing this controller // from another controller and we need to merge it's ViewData with this one // since this one will be empty. ProxyViewDataFeature? saveViewData = HttpContext.Features.Get(); if (saveViewData != null) { foreach (KeyValuePair kv in saveViewData.ViewData) { ViewData[kv.Key] = kv.Value; } } // continue normally await next(); break; } } /// /// Gets an action result based on the template name found in the route values and a model. /// /// The type of the model. /// The model. /// The action result. /// /// If the template found in the route values doesn't physically exist, Umbraco not found result is returned. /// protected override IActionResult CurrentTemplate(T model) { if (EnsurePhysicalViewExists(UmbracoRouteValues.TemplateName) == false) { // no physical template file was found return new PublishedContentNotFoundResult(UmbracoContext); } return View(UmbracoRouteValues.TemplateName, model); } private PublishedContentNotFoundResult GetNoTemplateResult(IPublishedRequest pcr) { // missing template, so we're in a 404 here // so the content, if any, is a custom 404 page of some sort if (!pcr.HasPublishedContent()) { // means the builder could not find a proper document to handle 404 return new PublishedContentNotFoundResult(UmbracoContext); } if (!pcr.HasTemplate()) { // means the engine could find a proper document, but the document has no template // at that point there isn't much we can do return new PublishedContentNotFoundResult( UmbracoContext, "In addition, no template exists to render the custom 404."); } return new PublishedContentNotFoundResult(UmbracoContext); } }