首页 > 解决方案 > 在 ASP.NET Core 3.1 中运行时渲染视图

问题描述

我们正在将我们的 ASP.NET Core 1.0 Web 应用程序升级到 ASP.NET Core 3.1。
现在对我们不起作用的一件事是我们的导出服务,它用于从 cshtml 文件生成 PDF。此代码用于 ASP.NET Core 1 应用程序。这是将 cshtml 文件呈现为字符串的代码:

public async Task<string> Export(object viewModel, string reportName, eExportType exportType, PdfExportOptions options)
    {
        using (var writer = new StringWriter())
        {
            var viewEngine = _context.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
            
            // Find the view
            var viewResult  = viewEngine.GetView($"~/Views/Reports/{reportName}.cshtml", $"~/Views/Reports/{reportName}.cshtml", false);

            if (viewResult?.Success == false) // This is the line that's not working: Success equals false
            {
                throw new Exception($"The report {reportName} could not be found.");
            }

            // Build ViewContext
            var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
            {
                Model = viewModel
            };
            var tempData = new TempDataDictionary(_context, new SessionStateTempDataProvider(null));
            var routingFeature = _context.Features[typeof(IRoutingFeature)] as RoutingFeature;
            var actionContext = new ActionContext(_context, routingFeature?.RouteData, new ActionDescriptor());
            var viewContext = new ViewContext(actionContext, viewResult?.View, viewData, tempData, writer, new HtmlHelperOptions());

            // Render the view
            if (viewResult?.View != null)
                await viewResult.View?.RenderAsync(viewContext);

            // Invoke the node.js service
            var htmlContent = writer.GetStringBuilder().ToString();
            var nodeServiceName = $"./NodeServices/{exportType.ToString().ToLower()}";
            //var exportHandler = GetExportHandler(exportType, options); // TODO: Currently only works with PDF. Use export handlers for different export types
            var result = await _nodeServices.InvokeAsync<byte[]>(nodeServiceName, htmlContent, options);
            
            return Convert.ToBase64String(result);
        }
    }

由于某种原因 viewResult?.Success 总是错误的。我已经尝试了各种组合以使其发挥作用,例如viewEngine.GetView($"~/Views/Reports/{reportName}.cshtml", $"~/Views/Reports/{reportName}.cshtml", false)

viewEngine.GetView($"~/Views/Reports/{reportName}.cshtml", $"{reportName}.cshtml", false),  
viewEngine.GetView($"Views/Reports/{reportName}.cshtml", $"{reportName}.cshtml", false)  

等等,但它们都不起作用,Success 始终为 false,并且属性 viewResult.View 始终为空。
我在 StackOverflow 上看过很多帖子,比如这个和其他很多帖子,但没有一个能解决我们的问题。我怀疑我们在 Startup 类中可能做错了什么,因为上面的变量 routingFeature 也是空的,但我对此并不完全确定。但只是为了确保正确配置了 Startup 类,下面是它的代码:

public class Startup
{
    public Startup(/*IHostingEnvironment env,*/ IConfiguration configuration)
    {
        Configuration = (IConfigurationRoot)configuration;
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions();
        services.Configure<SmsProvidersSettings>(Configuration.GetSection("SmsProvidersSettings"));
        // More configuration sections here, removed for brevity

        services.AddControllers().AddNewtonsoftJson(options =>
        {
            // Use the default property (Pascal) casing
            options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        });
        services.AddAuthentication(IISDefaults.AuthenticationScheme);
        services.AddAuthorization();

        var sessionTimeout = Convert.ToDouble(Configuration.GetSection("GlobalSettings")["SessionTimeout"]);
        var manager = new ApplicationPartManager();
        manager.ApplicationParts.Add(new AssemblyPart(typeof(Startup).Assembly));
        services.AddSingleton(manager);
        services.AddDistributedMemoryCache();
        services.AddSession(options => options.IdleTimeout = TimeSpan.FromSeconds(sessionTimeout));
        ////services.AddMvc(options =>
        ////{
        ////    options.Filters.Add(new UserActionAuditFilterAttribute(new AuditHandler(Configuration["ConnectionStrings:Audit"])));
        ////    options.Filters.Add(new ApiValidationFilterAttribute());
        ////    options.Filters.Add(new GlobalExceptionFilter());
        ////});
        //services.AddMemoryCache();
        services.AddNodeServices(options => options.InvocationTimeoutMilliseconds = 60000);

        services.Configure<RequestLocalizationOptions>(opts =>
        {
            var supportedCultures = new List<CultureInfo>
            {
                new CultureInfo("he-IL")
            };

            opts.DefaultRequestCulture = new RequestCulture("he-IL", "he-IL");
            opts.SupportedCultures = supportedCultures;
            opts.SupportedUICultures = supportedCultures;
        });

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        if (Convert.ToBoolean(Configuration.GetSection("GlobalSettings")["UseScheduler"]))
        {
            services.AddHangfire(configuration =>
                configuration
                    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                    .UseSimpleAssemblyNameTypeSerializer()
                    .UseRecommendedSerializerSettings()
                    .UseSqlServerStorage(
                        Configuration["ConnectionStrings:DigitalRural"],
                        new SqlServerStorageOptions
                        {
                            CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                            QueuePollInterval = TimeSpan.Zero,
                            SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                            UseRecommendedIsolationLevel = true,
                            PrepareSchemaIfNecessary = true,    // Default value: true
                            EnableHeavyMigrations = true,       // Default value: false
                            UsePageLocksOnDequeue = true,
                            DisableGlobalLocks = false
                        }));
        }

        services.AddTransient<IRecurringJobManager, RecurringJobManager>();
        services.AddTransient<IActionSystemService, ActionSystemService>();
        // More serivces here, removed for brevity

        services.AddScoped<IUnitOfWork>(sp =>
        {
            var httpContextAccessor = sp.GetService<IHttpContextAccessor>();
            var currentUser = httpContextAccessor.HttpContext?.Session.GetJson<SessionUser>(SessionStateKeys.CurrentUser);
            int? userId = null;
            if (currentUser != null)
                userId = currentUser.UserId;

            return new UnitOfWork(new DigitalRuralContext(Configuration["ConnectionStrings:DigitalRural"], userId), httpContextAccessor);
        });
        //
        services.AddControllersWithViews();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime, IGlobalApplicationDataService applicationDataService)
    {
        //var applicationDataService = app.ApplicationServices.GetService(typeof(IGlobalApplicationDataService)) as IGlobalApplicationDataService;
        applicationDataService?.GetApplicationDataVM();

        app.Use(async (context, next) =>
        {
            if (context.Request.Path == "/")
            {
                // Don't cache index.html
                context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
                context.Response.Headers.Add("Pragma", "no-cache");
            }

            await next();

            // If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
            // Rewrite request to use app root
            if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
            {
                context.Request.Path = "/"; // Put your Angular root page here 
                await next();
            }
        });

        app.UseAuthentication();
        app.UseAuthorization();

        loggerFactory
            .AddSerilog();

        //// Ensure any buffered events are sent at shutdown
        //appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);

        //if (env.IsDevelopment())
        //{
        //    app.UseDeveloperExceptionPage();
        //    app.UseBrowserLink();
        //}
        //else
        //{
        //    app.UseExceptionHandler("/Home/Error");
        //}

        ////app.UseDefaultFiles();
        //app.UseStaticFiles(new StaticFileOptions
        //{
        //    OnPrepareResponse = context => { }
        //});


        ////app.UseMvc();

        if (Convert.ToBoolean(Configuration.GetSection("GlobalSettings")["UseScheduler"]))
        {
            app.UseHangfireDashboard("/scheduler", new DashboardOptions          // Will be available at http://localhost:60209/scheduler
            {
                Authorization = new[] { new HangfireDashbordAuthorizationFilter() }
            });
            app.UseHangfireServer(new BackgroundJobServerOptions { StopTimeout = TimeSpan.FromSeconds(10) });
        }

        var applicationServices = app.ApplicationServices;
        var httpContextAccessor = applicationServices.GetService<IHttpContextAccessor>();

        app.UseFileServer(new FileServerOptions { FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")), RequestPath = "", EnableDefaultFiles = true });

        app.UseRouting();
        app.UseAuthorization();
        app.UseSession();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });
    }
}

那么你猜猜为什么我们的代码不起作用?

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

解决方案


推荐阅读