HTML 在生产环境中找不到模板 (NET Core 3.1)

HTML templates could not be found in Production (NET Core 3.1)

我有一个 Web 应用程序,它应该在注册时向用户发送验证电子邮件。我正在使用驻留在根目录下的文件夹 (EmailTemplate) 中的电子邮件模板。

问题是当我在 Google Cloud Platform 上部署版本时,我在注册时遇到此错误

模板在本地主机调试测试中运行良好,但在云测试中运行不正常。

.csproj 文件

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <UserSecretsId>removed intentionally</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <None Include="app.yaml" CopyToOutputDirectory="Always" />
  </ItemGroup>
 
  <ItemGroup>
    <Compile Remove="Repos\**" />
    <Content Remove="Repos\**" />
    <EmbeddedResource Remove="Repos\**" />
    <None Remove="Repos\**" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.21" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.21" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.21" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.21">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="3.1.15" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.5" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.5" />
  </ItemGroup>

</Project>

正在使用模板的电子邮件服务

 public class EmailService : IEmailService
    {
       
        private const string templatePath = @"EmailTemplate/{0}.html";
        private readonly SMTPConfigModel _smtpConfig;

        public EmailService(IOptions<SMTPConfigModel> smtpConfig)
        {
            _smtpConfig = smtpConfig.Value;
        }

        public async Task SendTestEmail(EmailDataModel testEmailModel)
        {
            testEmailModel.Subject = FillPlaceholders("Hello {{UserName}} this test subject", testEmailModel.PlaceHolders);
            testEmailModel.Body = FillPlaceholders(EmailBodyTemplateGetter("TestEmail"), testEmailModel.PlaceHolders);
            await SendEmail(testEmailModel);
            
        }

        public async Task SendVerificationEmail(EmailDataModel emailVerifyModel)
        {
            emailVerifyModel.Subject = FillPlaceholders("Please verify your email", emailVerifyModel.PlaceHolders);
            emailVerifyModel.Body = FillPlaceholders(EmailBodyTemplateGetter("EmailVerification"), emailVerifyModel.PlaceHolders);
            await SendEmail(emailVerifyModel);
        }

        private async Task SendEmail(EmailDataModel email)
        {
            MailMessage mail = new MailMessage
            {
                Subject = email.Subject,
                Body = email.Body,
                From = new MailAddress(_smtpConfig.SenderAddress, _smtpConfig.SenderDisplayName),
                IsBodyHtml = _smtpConfig.IsBodyHTML
            };

            foreach (var item in email.ToEmails)
                mail.To.Add(item);

            NetworkCredential networkCredential = new NetworkCredential(_smtpConfig.UserName, _smtpConfig.Password);

            SmtpClient smtpClient = new SmtpClient
            {
                Host = _smtpConfig.Host,
                Port = _smtpConfig.Port,
                EnableSsl = _smtpConfig.EnableSSL,
                UseDefaultCredentials = _smtpConfig.UseDefaultCredentials,
                Credentials = networkCredential
            };

            mail.BodyEncoding = Encoding.Default;
            await smtpClient.SendMailAsync(mail);
        }

        private string EmailBodyTemplateGetter(string templateName)
        {
 =========> string filepath = string.Format(templatePath, templateName); <==========
            var body = File.ReadAllText(filepath);
            return body;
        }

        private string FillPlaceholders(string text, List<KeyValuePair<string, string>> keyValuePairs)
        {
            if (!string.IsNullOrEmpty(text) && keyValuePairs != null)
                foreach (var placeholder in keyValuePairs)
                    if (text.Contains(placeholder.Key))
                        text = text.Replace(placeholder.Key, placeholder.Value);
            return text;
        }

    }

class中的调用方法Register.cs

 public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = new PtUser { UserName = Input.Email, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName };
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

                    EmailDataModel emailDataModel = new EmailDataModel() 
                    {
                        ToEmails = new List<string>() { Input.Email },
                        PlaceHolders = new List<KeyValuePair<string, string>>()
                        {
                            new KeyValuePair<string, string>("{{UserName}}", user.FirstName),
                            new KeyValuePair<string, string>("{{Link}}", callbackUrl)
                        }
                    };

             =======> await _emailService.SendVerificationEmail(emailDataModel); <============


                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

根据 asp.net core 3.1 可以通过相对于 Web 根目录的路径访问静态文件。因此在生产环境中无法访问 wwwroot 之外的任何文件。在这种情况下,您必须将 EmailTemplate 文件夹移动到 wwwroot 中,如下图所示:

And then in Startup.Configure you have to add reference of this middleware app.UseStaticFiles(); while you would browse the directory you have to modify code like this way

Note: In that case your path would be like wwwroot/EmailTemplate/EmailTemplate.html and your code should be like : private const string templatePath = @"wwwroot/EmailTemplate/{0}.html";

希望以上步骤能相应地指导您。