如何以纯字符串形式获取视图文件内容

How to get view file content as plain string

ASP.NET Core 5 MVC 应用程序使用来自 https://github.com/adoconnection/RazorEngineCore 的 Razor 引擎在运行时将视图呈现为字符串。

如何以字符串形式查看 ~/Views/Checkout/Order.cshtml 内容?

我试过了

ReadTemplate("~/Views/Checkout/Order.cshtml")

但是它抛出一个错误。应用程序可以部署为单个文件或单独的文件。

string ReadTemplate(string filename)
{
    var stream = GetType().Assembly.GetManifestResourceStream(filename);

    if (stream == null)
        throw new ApplicationException(filename + " view not found");

    StreamReader reader = new(stream);
    string template = reader.ReadToEnd();
    reader.Close();
    return template;
}

在您的评论回复中您这样写:

How to force view to be included as assembly resource ? It is used only by application as string content to render. It is not regular view, not used to create html page. Is it sufficient to set build action as Embedded Resource. Which path should used to get this resource?

您可以 infer the resource stream name yourself, or you can manually specify a name 通过直接编辑您的 .csproj 文件指定一个 <LogicalName>(我不知道为什么这个元素没有暴露在属性 window,确实应该如此)。后一种选择更好,因为在移动文件时名称将保持不变。

How to get view ~/Views/Checkout/Order.cshtml content as string?

您可以尝试使用以下代码创建一个 ViewRenderService:

public interface IViewRenderService
{
    Task<string> RenderToString(string viewName, object model);
}
public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IHttpContextAccessor _contextAccessor;

    public ViewRenderService(IRazorViewEngine razorViewEngine,
                             ITempDataProvider tempDataProvider,
                             IHttpContextAccessor contextAccessor)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _contextAccessor = contextAccessor;
    }

    public async Task<string> RenderToString(string viewName, object model)
    {
        var actionContext = new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ActionDescriptor());

        await using var sw = new StringWriter();
        var viewResult = FindView(actionContext, viewName);

        if (viewResult == null)
        {
            throw new ArgumentNullException($"{viewName} does not match any available view");
        }

        var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
        {
            Model = model
        };

        var viewContext = new ViewContext(
            actionContext,
            viewResult,
            viewDictionary,
            new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
            sw,
            new HtmlHelperOptions()
        );

        await viewResult.RenderAsync(viewContext);
        return sw.ToString();
    }

    private IView FindView(ActionContext actionContext, string viewName)
    {
        var getViewResult = _razorViewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
        if (getViewResult.Success)
        {
            return getViewResult.View;
        }

        var findViewResult = _razorViewEngine.FindView(actionContext, viewName, isMainPage: true);
        if (findViewResult.Success)
        {
            return findViewResult.View;
        }

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

        throw new InvalidOperationException(errorMessage);
    }
}

在上述服务中添加以下引用:

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.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;

然后,在Startup.ConfigureServices方法中注册ViewRenderService:

services.AddScoped<IViewRenderService, ViewRenderService>();

之后,您可以使用此服务将视图页面呈现为字符串:

public class HomeController : Controller
{ 
    private readonly IViewRenderService _viewRenderService;
    public HomeController(IViewRenderService viewRenderService)
    {  
        _viewRenderService = viewRenderService;
    }
     
    public async Task<IActionResult> CategoryIndex()
    {  
       //The model will be transferred to the View page.
        var stulist = new List<Student>() { new Student() { StudentName = "AA" }, new Student() { StudentName = "BB" } };

        var result =  _viewRenderService.RenderToString("Views/Student/Index.cshtml", stulist).Result;

        return View();
    }

结果是这样的: