Как-то так?
Интерфейс
using System.Threading.Tasks;
namespace DL.SO.Framework.Mvc.ViewEngine
{
public interface IRazorViewRenderer
{
Task<string> RenderToStringAsync<T>(T model, string viewPath);
}
}
Реализация
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.Options;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace DL.SO.Framework.Mvc.ViewEngine
{
public class SimpleRazorViewRenderer : IRazorViewRenderer
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IOptions<MvcViewOptions> _viewOptions;
public SimpleRazorViewRenderer(IHttpContextAccessor httpContextAccessor,
IRazorViewEngine razorViewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider,
IOptions<MvcViewOptions> viewOptions)
{
_httpContextAccessor = httpContextAccessor;
_razorViewEngine = razorViewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
_viewOptions = viewOptions;
}
public async Task<string> RenderToStringAsync<T>(T model, string viewPath)
{
var actionContext = GetActionContext();
var view = FindView(actionContext, viewPath);
using (var sw = new StringWriter())
{
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<T>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
sw,
_viewOptions.Value.HtmlHelperOptions
);
await view.RenderAsync(viewContext);
return sw.ToString();
}
}
private IView FindView(ActionContext actionContext, string viewPath)
{
var getViewResult = _razorViewEngine.GetView(null, viewPath, true);
if (getViewResult.Success)
{
return getViewResult.View;
}
var findViewResult = _razorViewEngine.FindView(actionContext, viewPath, 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 '{ viewPath }'. The following locations were searched:" }.Concat(searchedLocations)); ;
throw new InvalidOperationException(errorMessage);
}
private ActionContext GetActionContext()
{
var httpContext = _httpContextAccessor.HttpContext;
var routingFeature = httpContext.Features.Get<IRoutingFeature>();
return new ActionContext(
httpContext,
routingFeature.RouteData,
new ActionDescriptor());
}
}
}
Регистрация при запуске
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IRazorViewRenderer, SimpleRazorViewRenderer>();
...
}
Как это использовать
namespace DL.SO.Web.UI.Controllers
{
public class HomeController : Controller
{
private readonly IEmailService _emailService;
private readonly IRazorViewRenderer _viewRenderer;
public HomeController(IEmailService emailService,
IRazorViewRenderer viewRenderer)
{
_emailService = emailService;
_viewRenderer = viewRenderer;
}
public IActionResult Contact()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Contact(ContactViewModel model)
{
if (!ModelState.IsValid)
{
return Json(new {
Status = false,
Message = "Something wrong"
});
}
// I have the template/partial view under Shared/EmailTemplates folder
// but you can put it right in the Shared folder too
const string emailTemplatePath = "EmailTemplates/_ContactEmail";
var emailModel = new ContactEmailModel
{
VisitorFullName = model.FullName,
VisitorEmail = model.Email,
Company = model.Company,
Message = model.Message
};
// Calling the view renderer to parse the partial view into
// string
var emailContent = await _viewRenderer.RenderToStringAsync(
emailModel, emailTemplatePath);
// Send out email with my custom email service
_emailService.SendAsync("Customer Message", emailContent,
model.Email, "to@sample.com");
return Json(new {
Status = true,
Message = @"Your message is sent and you should receive
a confirmation email."
});
}
}
}
Частичное представление / Шаблон _ContactEmail.cshtml
@model DL.SO.Web.UI.EmailModels.Contact.ContactEmailModel
@{
Layout = null;
}
<p>Hello @Model.VisitorFullName,</p>
<p>
Thanks for contacting us. We have received your message and will
try to response to you as soon as possible.
</p>
<p>
Below is your message summary:
</p>
<ul>
<li><small>Your name: </small> @Model.VisitorFullName</li>
<li><small>Email: </small> @Model.VisitorEmail</li>
@if (!String.IsNullOrEmpty(Model.Company))
{
<li><small>Company: </small> @Model.Company</li>
}
<li><small>Message: </small> @Model.Message</li>
</ul>
<p>
Thank you,<br />
David Liang
</p>