Render a Razor Page to string : <h2>Problem:</h2>

<p>I need to render a Razor Page partial to a string.</p>

<h2>Why I want this:</h2>

<p>I want to create a controller action that responds with JSON containing a partial view and other optional parameters.</p>

<h2>Attempts:</h2>

<p>I am familiar with the following example that renders a View to a string: <a href="https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs" rel="noreferrer">https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs</a></p>

<p>However, it is not compatible with Pages, as it only searches in the Views directory, so even if I give it an absolute path to the partial it tries to locate my _Layout.cshtml (which it shouldn't even do!) and fails to find it.</p>

<p>I have tried to modify it so it renders pages, but I end up getting a NullReferenceException for ViewData in my partial when attempting to render it. I suspect it has to do with NullView, but I have no idea what to put there instead (the constructor for RazorView requires many objects that I don't know how to get correctly).</p>

<p>The code:</p>

<pre><code>// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0
// Modified by OronDF343: Uses pages instead of views.

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Routing;

namespace TestAspNetCore.Services
{
    public class RazorPageToStringRenderer
    {
        private readonly IRazorViewEngine _viewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;

        public RazorPageToStringRenderer(
            IRazorViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _viewEngine = viewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public async Task&lt;string&gt; RenderPageToStringAsync&lt;TModel&gt;(string viewName, TModel model)
        {
            var actionContext = GetActionContext();
            var page = FindPage(actionContext, viewName);

            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext(actionContext,
                                                  new NullView(),
                                                  new ViewDataDictionary&lt;TModel&gt;(new EmptyModelMetadataProvider(),
                                                                                 new ModelStateDictionary())
                                                  {
                                                      Model = model
                                                  },
                                                  new TempDataDictionary(actionContext.HttpContext,
                                                                         _tempDataProvider),
                                                  output,
                                                  new HtmlHelperOptions());

                page.ViewContext = viewContext;
                await page.ExecuteAsync();

                return output.ToString();
            }
        }

        private IRazorPage FindPage(ActionContext actionContext, string pageName)
        {
            var getPageResult = _viewEngine.GetPage(executingFilePath: null, pagePath: pageName);
            if (getPageResult.Page != null)
            {
                return getPageResult.Page;
            }

            var findPageResult = _viewEngine.FindPage(actionContext, pageName);
            if (findPageResult.Page != null)
            {
                return findPageResult.Page;
            }

            var searchedLocations = getPageResult.SearchedLocations.Concat(findPageResult.SearchedLocations);
            var errorMessage = string.Join(
                Environment.NewLine,
                new[] { $"Unable to find page '{pageName}'. The following locations were searched:" }.Concat(searchedLocations));

            throw new InvalidOperationException(errorMessage);
        }

        private ActionContext GetActionContext()
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
        }
    }
}
</code></pre>
