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
相关产品推荐
相关产品推荐

