You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MimeKit发送网站重置密码邮件失败的问题求助

忘记密码邮件发送功能故障排查

我正在为网站实现「忘记密码」邮件发送功能,计划通过替换HTML模板内容的方式向用户发送随机生成的新凭证,但目前无法成功发送任何邮件。已确认SMTP凭据有效(其他已上线的老应用使用相同凭据可以正常发送邮件,只是这些应用采用了不同的邮件发送方式)。

MailService.cs代码实现

using MimeKit;
using WebApi.Data;
using WebApi.Interfaces;

namespace WebApi.Services;
public class MailService : IMailService
{
    private readonly IConfiguration _configuration;
    private readonly OperationsManagerDbContext _context;
    public MailService(OperationsManagerDbContext operationsManagerDbContext, IConfiguration configuration)
    {
        _context = operationsManagerDbContext;
        _configuration = configuration;
    }

    private async Task<MailKit.Net.Smtp.SmtpClient> GetSmtpClient()
    {
        var smtpClient = new MailKit.Net.Smtp.SmtpClient();
        await smtpClient.ConnectAsync(_configuration["Smtp:Host"], Convert.ToInt32(_configuration["Smtp:Port"]), MailKit.Security.SecureSocketOptions.StartTls);
        await smtpClient.AuthenticateAsync(_configuration["Smtp:Username"], _configuration["Smtp:Password"]);
        return smtpClient;
    }

    async Task IMailService.SendEmailAsync(List<string> toEmail, string subject, string textPart, string htmlPart, string? fromEmail)
    {
        var mail = new MimeMessage();
        mail.From.Add(new MailboxAddress(_configuration["Smtp:FromName"], _configuration["Smtp:FromEmail"]));
        foreach (var email in toEmail)
        {
            mail.To.Add(new MailboxAddress("", email));
        }

        mail.Subject = subject;
        
        var multipart = new Multipart("mixed");
        var multipartAlt = new MultipartAlternative();

        var plain = new TextPart("plain");
        plain.SetText(System.Text.Encoding.UTF8, textPart);
        multipartAlt.Add(plain);

        var html = new TextPart("html");
        html.SetText(System.Text.Encoding.UTF8, htmlPart);
        multipartAlt.Add(html);
  
        multipart.Add(multipartAlt);

        multipart.Add(multipartAlt);

        mail.Body = multipart;

        using (var client = await GetSmtpClient())
        {
            await client.SendAsync(mail);
            await client.DisconnectAsync(true);
        }
    }

}

运行时错误信息

2025-05-09 23:18:32.951 [ 1:    Error]
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware - An unhandled exception has occurred while executing the request.
Stack Trace:
   at MailKit.Net.Smtp.SmtpClient.ParseMessageDataResponse(MimeMessage message, SmtpResponse response)
   at MailKit.Net.Smtp.SmtpClient.MessageDataAsync(FormatOptions options, MimeMessage message, Int64 size, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.Net.Smtp.SmtpClient.SendAsync(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, CancellationToken cancellationToken, ITransferProgress progress)
   at MailKit.Net.Smtp.SmtpClient.SendAsync(FormatOptions options, MimeMessage message, MailboxAddress sender, IList`1 recipients, CancellationToken cancellationToken, ITransferProgress progress)
   at WebApi.Services.MailService.WebApi.Interfaces.IMailService.SendEmailAsync(List`1 toEmail, String subject, String textPart, String htmlPart, String fromEmail) in F:\Work\Repos\Dev_Web\Angular\Emqube\operationsmanager\WebApi\Services\MailService.cs:line 55
   at WebApi.Services.AuthService.ForgotPassword(SiteUserForgotPasswordRequest siteUserForgotPasswordRequest) in F:\Work\Repos\Dev_Web\Angular\Emqube\operationsmanager\WebApi\Services\AuthService.cs:line 207
   at WebApi.Controllers.AuthController.ForgotPassword(SiteUserForgotPasswordRequest siteUserForgotPasswordRequest) in F:\Work\Repos\Dev_Web\Angular\Emqube\operationsmanager\WebApi\Controllers\AuthController.cs:line 53
   at lambda_method240(Closure, Object)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

已尝试的操作

曾尝试直接设置纯文本内容作为邮件正文,代码如下,但仍出现相同错误:

mail.Body = new TextPart("plain")
{
    Text = "some text"
};

排查指引

  • 修复重复的MIME部件添加:代码中multipart.Add(multipartAlt);被执行了两次,这会导致邮件的MIME结构无效,SMTP服务器大概率会拒绝这种格式。删除其中一次重复的添加操作。
  • 启用MailKit调试日志:在GetSmtpClient方法中添加以下代码,获取SMTP交互的详细日志,以便定位服务器返回的具体错误:
    smtpClient.LogLevel = MailKit.LogLevel.Debug;
    smtpClient.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; // 开发环境临时使用,生产环境需验证证书
    
    同时配置日志框架输出调试级别的日志,查看SMTP会话的每一步响应。
  • 验证发件人地址配置:确保_configuration["Smtp:FromName"]和_configuration["Smtp:FromEmail"]的取值合法,FromName避免为空或包含特殊字符,FromEmail必须是SMTP服务器允许发送的地址(部分服务器要求发件地址与认证用户名一致)。
  • 调整收件人地址格式:部分SMTP服务器不接受空的显示名称,尝试修改收件人添加代码为:
    mail.To.Add(new MailboxAddress(email, email));
    
  • 测试极简邮件结构:用最简化的邮件内容测试发送,排除复杂结构的影响,示例代码:
    var mail = new MimeMessage();
    mail.From.Add(new MailboxAddress("测试发件人", "your-valid-from-email@domain.com"));
    mail.To.Add(new MailboxAddress("测试收件人", "test-recipient@domain.com"));
    mail.Subject = "测试邮件";
    mail.Body = new TextPart("plain") { Text = "测试内容" };
    
  • 确认SMTP加密方式:检查SecureSocketOptions是否匹配服务器要求,部分服务器可能需要SslOnConnect而非StartTls,尝试切换加密方式测试。
  • 检查邮件大小:如果HTML模板内容过大,可能触发SMTP服务器的大小限制,先测试小内容的邮件是否能发送成功。

内容的提问来源于stack exchange,提问作者Asad Koths

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:14:52