umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
Other
4.49k stars 2.69k forks source link

(10RC4) RenderTemplateAsync fails (null exception in UmbracoViewPage) #12526

Closed skttl closed 2 years ago

skttl commented 2 years ago

Which exact Umbraco version are you using? For example: 9.0.1 - don't just write v9

10.0.0-rc4

Bug summary

In Full Text Search I use the RenderTemplateAsync method from IUmbracoComponentRenderer, to render a node with its template, in order to extract the html content for indexing.

https://github.com/skttl/umbraco-fulltextsearch8/blob/v9/dev/src/Our.Umbraco.FullTextSearch/Services/CacheService.cs#L74

In 10rc4, this doesn't work, when I use RenderTemplateAsync in a NotificationHandler - I get a NullReferenceException from WriteUmbracoContent in UmbracoViewPage (https://github.com/umbraco/Umbraco-CMS/blob/v10/contrib/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs#L131). The Context is null, and it can't read either the Request nor the Content Type.

Is this a bug, or do I need to initialize something to make this work in v10?

Specifics

When using RenderTemplateAsync from IUmbracoComponentRenderer, it breaks because of a NullReferenceException in UmbracoViewPage.

<!-- Error rendering template with id 1097: 'System.NullReferenceException: Object reference not set to an instance of an object. at Umbraco.Extensions.StringExtensions.InvariantContains(String compare, String compareTo) at Umbraco.Cms.Web.Common.Views.UmbracoViewPage`1.WriteUmbracoContent(TagHelperOutput tagHelperOutput) at Umbraco.Cms.Web.Common.Views.UmbracoViewPage`1.Write(Object value) at AspNetCore.Views_master.<>c__DisplayClass16_0.<<ExecuteAsync>b__0>d.MoveNext() in C:\Workspaces\Umbraco\Demos\10test\v10demo\Views\master.cshtml:line 23 --- End of stack trace from previous location --- at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() at AspNetCore.Views_master.ExecuteAsync() in C:\Workspaces\Umbraco\Demos\10test\v10demo\Views\master.cshtml:line 10 at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) at Umbraco.Cms.Web.Common.Templates.TemplateRenderer.ExecuteTemplateRendering(TextWriter sw, IPublishedRequest request) at Umbraco.Cms.Web.Common.Templates.TemplateRenderer.RenderAsync(Int32 pageId, Nullable`1 altTemplateId, StringWriter writer) at Umbraco.Cms.Core.Templates.UmbracoComponentRenderer.RenderTemplateAsync(Int32 contentId, Nullable`1 altTemplateId)' -->

Steps to reproduce

First I tried making a simple reproduction, by simply adding a template, which should render the template of another node, like this:

@using Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@inject Umbraco.Cms.Core.Templates.IUmbracoComponentRenderer _umbracoComponentRenderer;
@{
    Layout = null;
}
@Html.Raw(_umbracoComponentRenderer.RenderTemplateAsync(1126).Result.ToString())

I then opened the url https://localhost:44396/?altTemplate=renderTemplate, but found it to be working. Makes sense, as this request has a Context.

So I tried doing a simple reproduction in a notification handler with this:

using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services.Changes;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Templates;
using Umbraco.Extensions;

namespace v10demo
{
    public class LogHtmlOnPublish : INotificationHandler<ContentCacheRefresherNotification>
    {
        private ILogger<LogHtmlOnPublish> _logger;
        private IUmbracoComponentRenderer _umbracoComponentRenderer;

        public LogHtmlOnPublish(
            ILogger<LogHtmlOnPublish> logger,
            IUmbracoComponentRenderer umbracoComponentRenderer
            )
        {
            _logger = logger;
            _umbracoComponentRenderer = umbracoComponentRenderer;

        }
        public void Handle(ContentCacheRefresherNotification notification)
        {
            if (notification.MessageType != MessageType.RefreshByPayload)
                return;

            foreach (var payload in (ContentCacheRefresher.JsonPayload[])notification.MessageObject)
            {
                if (!payload.ChangeTypes.HasType(TreeChangeTypes.Remove) && !payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
                {
                    var htmlContent = _umbracoComponentRenderer.RenderTemplateAsync(payload.Id).Result.ToString();
                    _logger.LogInformation("{htmlContent}", htmlContent);
                }
            }
        }
    }
}

and added it to Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddUmbraco(_env, _config)
                .AddBackOffice()
                .AddWebsite()
                .AddComposers()
                .AddNotificationHandler<ContentCacheRefresherNotification, LogHtmlOnPublish>()
                .Build();
        }

This should log the html content of every page being published in the backoffice. So I went and published a page, and BAM - I got this in my log:

"<!-- Error rendering template with id 1097: 'System.NullReferenceException: Object reference not set to an instance of an object. at Umbraco.Extensions.StringExtensions.InvariantContains(String compare, String compareTo) at Umbraco.Cms.Web.Common.Views.UmbracoViewPage`1.WriteUmbracoContent(TagHelperOutput tagHelperOutput) at Umbraco.Cms.Web.Common.Views.UmbracoViewPage`1.Write(Object value) at AspNetCore.Views_master.<>c__DisplayClass16_0.<<ExecuteAsync>b__0>d.MoveNext() in C:\Workspaces\Umbraco\Demos\10test\v10demo\Views\master.cshtml:line 23 --- End of stack trace from previous location --- at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() at AspNetCore.Views_master.ExecuteAsync() in C:\Workspaces\Umbraco\Demos\10test\v10demo\Views\master.cshtml:line 10 at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) at Umbraco.Cms.Web.Common.Templates.TemplateRenderer.ExecuteTemplateRendering(TextWriter sw, IPublishedRequest request) at Umbraco.Cms.Web.Common.Templates.TemplateRenderer.RenderAsync(Int32 pageId, Nullable`1 altTemplateId, StringWriter writer) at Umbraco.Cms.Core.Templates.UmbracoComponentRenderer.RenderTemplateAsync(Int32 contentId, Nullable`1 altTemplateId)' -->"

Expected result / actual result

I would expect it to just work, just like it did in v9, but I guess the addition of the content type check in UmbracoViewPage.WriteUmbracoContent() might require some more changes.

bergmania commented 2 years ago

@skttl Thanks for reporting.. It's fixed in https://github.com/umbraco/Umbraco-CMS/pull/12550

nul800sebastiaan commented 2 years ago

Fixed in https://github.com/umbraco/Umbraco-CMS/pull/12550