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

ASP.NET Core部署IIS后用MailKit+Mailgun发邮件遇SMTP意外断开

ASP.NET Core部署IIS后MailKit发送邮件失败(本地正常)

本地运行ASP.NET Core应用时,使用MailKit调用Mailgun发送邮件完全正常,但部署到远程IIS服务器后,抛出异常:

Exception: Failed to send the email: The SMTP server has unexpectedly disconnected.

EmailSender类代码

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using MimeKit;
using MimeKit.Text;
using System;
using System.Threading.Tasks;
using MailKit.Security;

namespace syngri_web_app.Services
{
    public class EmailSender : IEmailSender
    {
        private readonly MailgunSettings _mailgunSettings;

        public EmailSender(IOptions<MailgunSettings> mailgunSettings)
        {
            _mailgunSettings = mailgunSettings.Value;
        }

        public async Task SendEmailAsync(string email, string subject, string htmlMessage)
        {
            try
            {
                var mimeMessage = new MimeMessage();
                mimeMessage.From.Add(new MailboxAddress("Excited Admin", "foo@" + _mailgunSettings.Domain));
                mimeMessage.To.Add(new MailboxAddress("Excited User", email));
                mimeMessage.Subject = subject;
                mimeMessage.Body = new TextPart(TextFormat.Html)
                {
                    Text = htmlMessage
                };

                using (var client = new MailKit.Net.Smtp.SmtpClient())
                {
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;

                    await client.ConnectAsync("smtp.mailgun.org", 587, SecureSocketOptions.StartTls);
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    await client.AuthenticateAsync("postmaster@"+_mailgunSettings.Domain+"", _mailgunSettings.ApiKey);

                    await client.SendAsync(mimeMessage);
                    await client.DisconnectAsync(true);
                }
            }
            catch (Exception ex)
            {
                // Log the exception details
                Console.WriteLine("An exception occurred while sending the email: " + ex.ToString());
                if (ex.InnerException != null)
                {
                    Console.WriteLine("Inner Exception: " + ex.InnerException.ToString());
                }
                throw new Exception("Failed to send the email: " + ex.Message);
            }
        }
    }
}

控制器相关代码

[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Forget(UserDto request)
{
    if (ModelState.IsValid)
    {
        var user = await userManager.FindByEmailAsync(request.Email);
        if (user != null && !user.EmailConfirmed)
        {
            ModelState.AddModelError("message", "Email not confirmed yet");
            ItoastNotification.Error("Something went Wrong");
            return View(request);
        }
        if (user == null) {
            ModelState.AddModelError("message", "Email not Exist");
            ItoastNotification.Error("Something went Wrong, Please inter valid email");
            return View(request);
        }
        var code = await userManager.GeneratePasswordResetTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Url.Action(
            "ResetPassword",
            "Account",
            new { area = "", code, request.Email },
            protocol: Request.Scheme
        );
        var emailMessage = $"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.";
        await emailSender.SendEmailAsync(request.Email, "Reset Password", emailMessage);

        ItoastNotification.Success("Reset Email Has been Sent Please Check your Email");
        return RedirectToAction("Login");
    }
    foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
    {
        // Handle or log the validation error messages
        var errorMessage = error.ErrorMessage;
        ItoastNotification.Error("Something went Wrong: "+errorMessage);
        break;
    }
    
    return View(request);
}

排查与解决建议

  • 检查服务器出站网络权限:确认IIS所在服务器的防火墙/安全组允许访问smtp.mailgun.org的587端口,多数生产服务器会默认拦截出站SMTP请求,需要运维手动开放。
  • 增加连接超时设置:在ConnectAsync方法中添加超时参数,避免因网络延迟导致连接断开,示例:
    await client.ConnectAsync("smtp.mailgun.org", 587, SecureSocketOptions.StartTls, 
        new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token);
    
  • 修正证书验证逻辑:当前代码跳过了SSL证书验证(ServerCertificateValidationCallback = (s, c, h, e) => true),生产环境中可能触发服务器安全策略拦截,建议移除该行,确保服务器信任Mailgun的SSL证书;若必须跳过,需确认服务器安全策略允许。
  • 检查Mailgun IP白名单:登录Mailgun后台,确认是否设置了IP白名单,将IIS服务器的公网IP加入白名单,否则Mailgun会拒绝非授权IP的连接请求。
  • 完善日志记录:将Console.WriteLine替换为正式的日志框架(如Serilog、NLog),记录完整的异常堆栈信息(包括InnerException),能更精准定位断开原因(如TLS握手失败、认证错误等)。

内容的提问来源于stack exchange,提问作者Muhammad Zahid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:27:52