首页 > 解决方案 > 指定剃须刀页面的绝对路径

问题描述

在尝试编写一个完全独立的控制台应用程序以将 Razor 页面呈现为字符串时,我一直在阅读类似“”的文章。

我独特的(它会出现)用例是我还想通过字符串输入而不是本地/Views文件夹来提供剃须刀页面。

虽然我有现成的存储 工作,但我无法指定从何处加载*.cshtml文件,因为我想在特定位置提供它们。

假设为了争论,它们(至少在开发期间)位于控制台项目文件夹上方的文件夹中,例如:

/sln
  sln.sln
  /console
    console.csproj
    Program.cs
  /razor
    /views
      MyView.cshtml

如果我从 repo 中转储所有相关代码,我可以运行程序,但是在执行剃刀页面时会出现错误:

One or more compilation failures occurred:
bkrmmc1j.kzc(4,20): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(5,19): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(4,82): error CS0518: Predefined type 'System.Type' is not defined or imported
bkrmmc1j.kzc(4,116): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(4,133): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(5,81): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(5,106): error CS0518: Predefined type 'System.Type' is not defined or imported
bkrmmc1j.kzc(9,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(10,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(11,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(12,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(13,11): error CS0246: The type or namespace name 'Microsoft' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(14,11): error CS0246: The type or namespace name 'Microsoft' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(15,11): error CS0246: The type or namespace name 'Microsoft' could not be found (are you missing a using directive or an assembly reference?)
bkrmmc1j.kzc(16,14): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(16,78): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(16,87): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(16,132): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(17,42): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(20,67): error CS1983: The return type of an async method must be void, Task or Task<T>
bkrmmc1j.kzc(20,39): error CS0400: The type or namespace name 'System' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(28,24): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(28,118): error CS0518: Predefined type 'System.Void' is not defined or imported
bkrmmc1j.kzc(30,24): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(30,71): error CS0518: Predefined type 'System.Void' is not defined or imported
bkrmmc1j.kzc(32,24): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(32,87): error CS0518: Predefined type 'System.Void' is not defined or imported
bkrmmc1j.kzc(34,24): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(34,83): error CS0518: Predefined type 'System.Void' is not defined or imported
bkrmmc1j.kzc(36,24): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(36,71): error CS0518: Predefined type 'System.Object' is not defined or imported
bkrmmc1j.kzc(36,102): error CS0518: Predefined type 'System.Void' is not defined or imported
bkrmmc1j.kzc(20,67): error CS0115: '_Views_MyView.ExecuteAsync()': no suitable method found to override
bkrmmc1j.kzc(27,18): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(29,18): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(31,18): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(33,18): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(35,18): error CS0400: The type or namespace name 'Microsoft' could not be found in the global namespace (are you missing an assembly reference?)
bkrmmc1j.kzc(22,13): error CS0518: Predefined type 'System.Object' is not defined or imported
bkrmmc1j.kzc(22,13): error CS0103: The name 'BeginContext' does not exist in the current context
bkrmmc1j.kzc(22,26): error CS0518: Predefined type 'System.Int32' is not defined or imported
bkrmmc1j.kzc(22,30): error CS0518: Predefined type 'System.Int32' is not defined or imported
bkrmmc1j.kzc(22,33): error CS0518: Predefined type 'System.Boolean' is not defined or imported
bkrmmc1j.kzc(23,13): error CS0518: Predefined type 'System.Object' is not defined or imported
bkrmmc1j.kzc(23,13): error CS0103: The name 'WriteLiteral' does not exist in the current context
bkrmmc1j.kzc(23,26): error CS0518: Predefined type 'System.String' is not defined or imported
bkrmmc1j.kzc(24,13): error CS0518: Predefined type 'System.Object' is not defined or imported
bkrmmc1j.kzc(24,13): error CS0103: The name 'EndContext' does not exist in the current context
bkrmmc1j.kzc(20,67): error CS0161: '_Views_MyView.ExecuteAsync()': not all code paths return a value

我认为这是因为该页面位于应用程序项目文件夹之外,所以问题是我该如何解决这个问题?是否可以动态创建剃须刀页面并允许加载它以执行?

更简单地说,如何将剃须刀页面的位置指定为解决方案或项目文件夹之外的位置?或者是否可以将剃刀页面作为字符串加载并提供给内存中的引擎?

我(几乎)工作的代码是这样的(从我发现的各种回购和文章中大量压缩):

程序.cs:

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
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;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.PlatformAbstractions;

namespace Razor2Pdf
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var str = RazorRenderer.DoHtml();
        }
    }

    public class MyModel { }

    public class RazorRenderer
    {
        public static string DoHtml()
        {
            var slnpath = $@"{Directory.GetCurrentDirectory()}\..\..\..\..";
            var razorpath = $@"{slnpath}\RazorPages";
            var viewname = "MyView";

            var renderer = GetRenderer();
            var html = renderer.RenderViewToString(razorpath, viewname, new MyModel());
            return html;
        }

        private static RazorViewToStringRenderer GetRenderer()
        {
            var services = new ServiceCollection();
            var applicationEnvironment = PlatformServices.Default.Application;
            services.AddSingleton(applicationEnvironment);

            var appDirectory = Directory.GetCurrentDirectory();

            var environment = new HostingEnvironment
            {
                ApplicationName = Assembly.GetEntryAssembly().GetName().Name
            };
            services.AddSingleton<IHostingEnvironment>(environment);

            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.FileProviders.Clear();
                options.FileProviders.Add(new PhysicalFileProvider(appDirectory));
                options.FileProviders.Add(new PhysicalFileProvider($@"{appDirectory}\..\..\..\..\RazorPages"));
            });

            services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

            var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
            services.AddSingleton<DiagnosticSource>(diagnosticSource);

            services.AddLogging();
            services.AddMvc();
            services.AddSingleton<RazorViewToStringRenderer>();

            var provider = services.BuildServiceProvider();
            return provider.GetRequiredService<RazorViewToStringRenderer>();
        }
    }
    public class RazorViewToStringRenderer
    {
        private IRazorViewEngine _viewEngine;
        private ITempDataProvider _tempDataProvider;
        private IServiceProvider _serviceProvider;

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

        public string RenderViewToString<TModel>(string razorpath, string viewName, TModel model)
        {
            var actionContext = GetActionContext();
            var view = FindView(actionContext, razorpath, viewName);

            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext(
                    actionContext,
                    view,
                    new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
                    {
                        Model = model
                    },
                    new TempDataDictionary(
                        actionContext.HttpContext,
                        _tempDataProvider),
                    output,
                    new HtmlHelperOptions());

                view.RenderAsync(viewContext);

                return output.ToString();
            }
        }

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

            var findViewResult = _viewEngine.FindView(actionContext, viewName, isMainPage: false);
            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);
        }

        private ActionContext GetActionContext()
        {
            var dict = new RouteValueDictionary();
            dict.Add("razor", @"C:\git\Dink2Pdf\ConsoleApp1\RazorPages");

            var httpContext = new DefaultHttpContext();
            httpContext.RequestServices = _serviceProvider;
            return new ActionContext(httpContext, new RouteData(dict), new ActionDescriptor());
        }
    }
}

MyView.cshtml:

@model Razor2Pdf.MyModel

Testing

标签: c#asp.net-mvcrazor.net-core

解决方案


推荐阅读