Razor Pages中如何将带ViewModel的页面渲染为HTML字符串用于邮件?
Solution for Rendering Razor Page to HTML String in ASP.NET Core
Step 1: Implement a Razor Page Rendering Service
Create a reusable service that handles executing the PageModel's logic and rendering the Razor page to a string. This ensures you reuse the existing OnGet data fetching and calculation logic.
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.ViewFeatures; using System.IO; using System.Threading.Tasks; public interface IRazorPageRenderer { Task<string> RenderPageToStringAsync<TModel>(string pagePath, TModel model) where TModel : class; } public class RazorPageRenderer : IRazorPageRenderer { private readonly IRazorViewEngine _viewEngine; private readonly ITempDataProvider _tempDataProvider; private readonly IServiceProvider _serviceProvider; private readonly IHttpContextAccessor _httpContextAccessor; public RazorPageRenderer(IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor) { _viewEngine = viewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; _httpContextAccessor = httpContextAccessor; } public async Task<string> RenderPageToStringAsync<TModel>(string pagePath, TModel model) where TModel : class { // Initialize context var httpContext = _httpContextAccessor.HttpContext ?? new DefaultHttpContext { RequestServices = _serviceProvider }; var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor()); // Locate the target Razor page var pageResult = _viewEngine.FindPage(actionContext, pagePath); if (!pageResult.Success) { throw new InvalidOperationException($"Page not found at path: {pagePath}"); } // Set up view data and temp data var viewData = new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model }; var tempData = new TempDataDictionary(httpContext, _tempDataProvider); // Prepare view context var viewContext = new ViewContext( actionContext, pageResult.Page, viewData, tempData, new StringWriter(), new HtmlHelperOptions() ); // Execute PageModel logic (OnGetAsync) if (pageResult.Page is PageModel pageModel) { pageModel.PageContext = new PageContext(actionContext); pageModel.ViewData = viewData; pageModel.TempData = tempData; pageModel.Url = new UrlHelper(actionContext); // Inject any required dependencies into the PageModel (e.g., database context) // Example: pageModel.DbContext = _serviceProvider.GetRequiredService<YourDbContext>(); await pageModel.OnGetAsync(); } // Render page to string await pageResult.Page.ExecuteAsync(viewContext); return ((StringWriter)viewContext.Writer).ToString(); } }
Step 2: Register the Service in Dependency Injection
Add the service to your app's DI container (in Program.cs for .NET 6+):
builder.Services.AddScoped<IRazorPageRenderer, RazorPageRenderer>(); builder.Services.AddHttpContextAccessor();
Step 3: Use the Service to Generate Invoice HTML
In your target page's PageModel, inject the renderer and call it to get the HTML string for the email body.
Assuming your layout-free invoice page is Pages/InvoicePrint.cshtml with PageModel InvoicePrintModel:
public class EmailSenderPageModel : PageModel { private readonly IRazorPageRenderer _pageRenderer; public EmailSenderPageModel(IRazorPageRenderer pageRenderer) { _pageRenderer = pageRenderer; } public async Task<IActionResult> OnPostSendInvoiceAsync(int invoiceId) { // Initialize the invoice PageModel and pass required parameters var invoiceModel = new InvoicePrintModel { InvoiceId = invoiceId }; // Render the invoice page to HTML string invoiceHtml = await _pageRenderer.RenderPageToStringAsync("/Pages/InvoicePrint", invoiceModel); // Use the HTML as email body (integrate with your email service) // Example: await _emailService.SendAsync("customer@example.com", "Your Invoice", invoiceHtml); return RedirectToPage("/Invoices"); } }
Critical Fixes for Null Reference Errors
- Inject PageModel Dependencies: If your
InvoicePrintModelrequires services (like a database context), resolve them via_serviceProviderin the renderer before callingOnGetAsync. - Route Data Handling: If your invoice page uses route parameters, populate
pageModel.PageContext.RouteData.Valueswith the required values in the renderer. - Confirm Layout-Free Setup: Ensure
InvoicePrint.cshtmlincludes@{ Layout = null; }to exclude site layouts from the email HTML.
内容的提问来源于stack exchange,提问作者David Bahler
相关产品推荐
相关产品推荐

