将 Razor 页面渲染为字符串 [英] Render a Razor Page to string

查看:25
本文介绍了将 Razor 页面渲染为字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将 Razor 页面呈现为字符串的一部分.

I need to render a Razor Page partial to a string.

我想创建一个控制器操作,该操作以包含部分视图和其他可选参数的 JSON 进行响应.

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

我熟悉以下将视图呈现为字符串的示例:https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs

I am familiar with the following example that renders a View to a string: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs

然而,它与 Pages 不兼容,因为它只在 Views 目录中搜索,所以即使我给它一个绝对路径到部分它也会尝试定位我的 _Layout.cshtml(它甚至不应该这样做!) 并且找不到它.

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.

我已经尝试修改它以呈现页面,但是当我尝试呈现它时,我最终在我的部分中为 ViewData 获得了 NullReferenceException.我怀疑它与 NullView 有关,但我不知道该放什么(RazorView 的构造函数需要许多我不知道如何正确获取的对象).

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).

代码:

// 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<string> RenderPageToStringAsync<TModel>(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<TModel>(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());
        }
    }
}

推荐答案

我就是这样做的.

一如既往地在 Startup.cs 中注册服务

As always register the Service in Startup.cs

services.AddScoped<IViewRenderService, ViewRenderService>();

服务定义如下:

public interface IViewRenderService
{
    Task<string> RenderToStringAsync<T>(string viewName, T model) where T : PageModel;
}

public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IServiceProvider _serviceProvider;
    private readonly IHttpContextAccessor _httpContext;
    private readonly IActionContextAccessor _actionContext;
    private readonly IRazorPageActivator _activator;


    public ViewRenderService(IRazorViewEngine razorViewEngine,
        ITempDataProvider tempDataProvider,
        IServiceProvider serviceProvider,
        IHttpContextAccessor httpContext,
        IRazorPageActivator activator,
        IActionContextAccessor actionContext)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;

        _httpContext = httpContext;
        _actionContext = actionContext;
        _activator = activator;

    }


    public async Task<string> RenderToStringAsync<T>(string pageName, T model) where T : PageModel
    {


        var actionContext =
            new ActionContext(
                _httpContext.HttpContext,
                _httpContext.HttpContext.GetRouteData(),
                _actionContext.ActionContext.ActionDescriptor
            );

        using (var sw = new StringWriter())
        {
            var result = _razorViewEngine.FindPage(actionContext, pageName);

            if (result.Page == null)
            {
                throw new ArgumentNullException($"The page {pageName} cannot be found.");
            }

            var view = new RazorView(_razorViewEngine,
                _activator,
                new List<IRazorPage>(),
                result.Page,
                HtmlEncoder.Default,
                new DiagnosticListener("ViewRenderService"));


            var viewContext = new ViewContext(
                actionContext,
                view,
                new ViewDataDictionary<T>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                {
                    Model = model
                },
                new TempDataDictionary(
                    _httpContext.HttpContext,
                    _tempDataProvider
                ),
                sw,
                new HtmlHelperOptions()
            );


            var page = ((Page)result.Page);

            page.PageContext = new Microsoft.AspNetCore.Mvc.RazorPages.PageContext
            {
                ViewData = viewContext.ViewData

            };

            page.ViewContext = viewContext;


            _activator.Activate(page, viewContext);

            await page.ExecuteAsync();


            return sw.ToString();
        }
    }



}

我是这样称呼的

  emailView.Body = await this._viewRenderService.RenderToStringAsync("Email/ConfirmAccount", new Email.ConfirmAccountModel
                {
                    EmailView = emailView,
                });

Email/ConfirmAccount"是我的 Razor 页面(Under pages)的路径.ConfirmAccountModel"是该页面的页面模型.

"Email/ConfirmAccount" is the path to my Razor page (Under pages). "ConfirmAccountModel" is my page model for that page.

ViewData为null,因为Page的ViewData是在设置PageContext的时候设置的,所以如果没有设置ViewData为null.

ViewData is null because the ViewData for the Page is set when the PageContext is set, so if this is not set ViewData is null.

我还发现我不得不打电话

I also found that I had to call

_activator.Activate(page, viewContext);

为了一切顺利.这尚未经过全面测试,因此可能不适用于所有场景,但应该可以帮助您入门.

For it all to work. This is not fully tested yet so may not work for all scenarios but should help you get started.

这篇关于将 Razor 页面渲染为字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆