使用生成的Razor视图发送电子邮件(MailKit)

3
希望我能简单地解释一下我的请求:
在 .Net Core 中,我们可以使用 View(FileName, Model) 发送模型数据后显示 .cshtml 视图。
是否有一种方法可以将 Model 发送到 .cshtml 文件,这样我们就不用显示结果视图,而是通过 Mailkit 以附件形式将其发送邮件。
1个回答

12

感谢Paris Polyzos和他的文章,我找到了解决方案,并希望分享出来,希望有人可以从中受益或改进它。

首先: 我们需要创建一个service将Rasor转换为字符串,代码Razor2String.cs如下:

using System
using System.IO;
using System.Threading.Tasks;
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.ViewFeatures;
using Microsoft.AspNetCore.Routing;
 
namespace Project.Utilities
{
    public interface IViewRenderService
    {
        Task<string> RenderToStringAsync(string viewName, object model);
    }
 
    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;
         public ViewRenderService(IRazorViewEngine razorViewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _razorViewEngine = razorViewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }
 
        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
 
            using (var sw = new StringWriter())
            {
                var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
                if (viewResult.View == null)
                {
                    throw new ArgumentNullException($"{viewName} does not match any available view");
                }
 
                var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                {
                    Model = model
                };
 
                var viewContext = new ViewContext(
                    actionContext,
                    viewResult.View,
                    viewDictionary,
                    new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                    sw,
                    new HtmlHelperOptions()
                );
 
                await viewResult.View.RenderAsync(viewContext);
                return sw.ToString();
            }
        }
    }
}

第二步:我们需要在 buildOptions 中添加 "preserveCompilationContext": true,这样 project.json 就变成了:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },
  "dependencies": {
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
    "Microsoft.AspNetCore.Mvc": "1.0.1",
    "MailKit":"1.10.0"
  },
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.1"
        }
      },
      "imports": "dnxcore50"
    }
  }
}

第三步:我们需要将服务添加到Startup类的ConfigureServices中,以便Startup.cs文件变为:

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Project.Utilities;

public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
             // Add application services.
            services.AddScoped<IViewRenderService, ViewRenderService>();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync(
                    "Hello World of the last resort. The Time is: " +
                    DateTime.Now.ToString("hh:mm:ss tt"));
            });
        }
    }

第四步: 定义你的Model,比如 users.cs

namespace myApp
{
     public class users {
        public string   UserId {get; set;}
        public string   UserName {get; set;}
    } 
}

第五步:在Views文件夹中创建View模板,例如Views/Razor2String.cshtml

Hello
<hr/>

@{
    ViewData["Title"] = "Contact";
}
<h2>@ViewData["Title"].</h2>
<h3>user id: @Model.UserId</h3>

第六步: Program.cs 简单来说就是这样的:
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;

namespace myApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseKestrel()
                .UseUrls("http://localhost:50000")
                .UseStartup<Startup>()
                .Build();
            host.Run();
        }
    }
}

第七步:主要部分,'Email.cs':

using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;

using Project.Utilities;
using System.Threading.Tasks;

using MailKit.Net.Smtp;   // for SmtpClient
using MimeKit;  // for MimeMessage, MailboxAddress and MailboxAddress

namespace myApp
{
    [Route("api")]
    public class RenderController : Controller
    {
        private readonly IViewRenderService _viewRenderService;
        public RenderController(IViewRenderService viewRenderService)
        {
            _viewRenderService = viewRenderService;
        }
     
      [Route("sendEmail")]
      public async Task sendEmail()
        {
            var viewModel = new users
            {
                UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
                UserName = "iggy",
            };
     
            // Get the generated Razor view as String
            var result = await _viewRenderService.RenderToStringAsync("Razor2String", viewModel);

            MemoryStream stream = new MemoryStream ();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write((String)result);
            writer.Flush();
            stream.Position = 0;

            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("Hasan Yousef", "mySelf@gmail.com"));
            message.To.Add(new MailboxAddress("Personal", "myPersonalEmail@gmail.com"));
            message.Subject = "Email Test";
            var bodyBuilder = new BodyBuilder();

            bodyBuilder.HtmlBody = @"<div>HTML email body</Div>";

            bodyBuilder.Attachments.Add ("msg.html", stream);

            message.Body = bodyBuilder.ToMessageBody();

            using (var client = new SmtpClient())
                {
                    client.Connect("smtp.gmail.com", 587);
                    client.AuthenticationMechanisms.Remove("XOAUTH2");  // due to enabling less secure apps access
                Console.WriteLine("Prepairing the Email");
                    try{
                        client.Authenticate("mySelf@gmail.com", "myPSWD");
                        Console.WriteLine("Auth Completed");
                    }
                    catch (Exception e){
                    Console.WriteLine("ERROR Auth"); 
                    }
                    try{
                        client.Send(message);
                        Console.WriteLine("Email had been sent");
                    }
                    catch (Exception e){
                        Console.WriteLine("ERROR");
                    }
                    client.Disconnect(true);
                }
        }
    }
}

如果您需要将字符串发送回浏览器,您可以使用:

  [Route("returnView")]
  public async Task<IActionResult> returnView()
    {
        var viewModel = new users
        {
            UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
            UserName = "iggy",
        };
 
        // Get the generated Razor view as String
        var result = await _viewRenderService.RenderToStringAsync("Razor2String", viewModel);
        return Content(result);
    }

如果你需要将结果发送到AJAX请求中,可以使用类似以下的代码:
public async Task<String> RenderInviteView()
{
.
.
.
return result;
}

如果你想要有单独的方法将字符串转换为流,就像GenerateStreamFromString(result)一样,那么可以使用using (Stream stream = GenerateStreamFromString(result)){ }来调用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接