使用嵌套托管将 MVC5 视图作为字符串获取

Bjø*_*ørn 5 c# asp.net-mvc-5

我有以下由 Hangfire 执行的代码(没有 HttpContext),当我在本地运行时运行完美:

class FakeController : ControllerBase
{
    protected override void ExecuteCore() { }

    public static string RenderViewToString(string controllerName, string viewName, object viewData)
    {
        using (var writer = new StringWriter())
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", controllerName);
            var fakeControllerContext = new ControllerContext(
                                                              new HttpContextWrapper(
                                                                                     new HttpContext(new HttpRequest(null, "http://nopage.no", null)
                                                                                                   , new HttpResponse(null))
                                                                                     ), 
                                                              routeData, 
                                                              new FakeController()
                                                            );
            var razorViewEngine = new RazorViewEngine();
            var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);

            var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, new ViewDataDictionary(viewData), new TempDataDictionary(), writer);
            razorViewResult.View.Render(viewContext, writer);
            return writer.ToString();

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,我们设置应用程序的方式如下:

https://application.net <- 一个应用程序

https://application.net/admin <- 运行此代码的另一个应用程序。

当我在 上运行代码时https://application.net/admin,出现以下异常:

System.ArgumentException: The virtual path '/' maps to another application, which is not allowed.
Run Code Online (Sandbox Code Playgroud)

它发生在这一行:razorViewEngine.FindView(fakeControllerContext, viewName, "", false)

我尝试创建自己的 RazorViewEngine 并覆盖了一些方法来查找视图,但无济于事。

class MyViewEngine : RazorViewEngine
{
    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        if (!base.FileExists(controllerContext, virtualPath))
            return base.FileExists(controllerContext, "~/../admin" + virtualPath.TrimStart('~'));

        return true;
    }
    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        var newViewPath = viewPath;
        if (!base.FileExists(controllerContext, viewPath))
            newViewPath = "~/../admin/" + viewPath.TrimStart('~');

        return base.CreateView(controllerContext, newViewPath, masterPath);
    }
}
Run Code Online (Sandbox Code Playgroud)

这失败了,因为它不想让我离开“目录树的最上层”。

是否有一个简单的解决方法,或者从剃刀视图创建字符串的替代方法?- 目的是创建电子邮件模板。会创建很多电子邮件,我不想使用 HttpClient 要求我自己的端点创建它。

Bjø*_*ørn 0

经过多次尝试和失败后,我最终放弃了我的方法,转而使用Rick Strahl 的 WestWind.RazorHosting库。

这是我使用的文档中最重要的部分。

这是我的代码最终的结果。

class FakeController : IDisposable
{
    private readonly RazorFolderHostContainer _host;
    public FakeController()
    {
        _host = new RazorFolderHostContainer
               {
                   TemplatePath = $@"{HostingEnvironment.ApplicationPhysicalPath}Views\EmailTemplate\"
               };

        _host.AddAssemblyFromType(typeof(Controller));
        _host.AddAssemblyFromType(typeof(EmailTemplateController.TestViewModel));
        _host.Start();
    }

    public string RenderViewToString(string viewName, object viewData)
    {
        return _host.RenderTemplate($@"~/{viewName}.cshtml", viewData);
    }

    public void Dispose()
    {
        _host.Stop();
    }
}
Run Code Online (Sandbox Code Playgroud)

EmailTemplate这会在我的文件夹中查找视图Views,查找我想要使用的 *.cshtml 文件。由于它从文件夹中获取它们Views,我仍然能够在普通控制器中使用相同的视图,因此当我也在端点中使用视图时它也会工作。漂亮。