使用C# SendGrid库发送邮件在测试环境报错求助
开发环境下用C# SendGrid库发送邮件完全正常,但部署到测试环境后,调用SendEmailAsync方法时抛出以下异常:
{"Message":"Erro: System.AggregateException: One or more errors occurred. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host\r\n at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)\r\n at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)\r\n --- End of inner exception stack trace ---\r\n at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)\r\n at System.Net.PooledStream.EndWrite(IAsyncResult asyncResult)\r\n at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)\r\n --- End of inner exception stack trace ---\r\n at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)\r\n at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)\r\n --- End of inner exception stack trace ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at SendGrid.Helpers.Reliability.RetryDelegatingHandler.d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at SendGrid.BaseClient.d__20.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at SendGrid.BaseClient.d__21.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at SendGrid.BaseClient.d__22.MoveNext()\r\n --- End of inner exception stack trace ---\n
最初怀疑是权限问题,但同一服务器上另一个仅用于发送邮件的站点能正常运行,说明问题出在当前项目本身,无法定位具体原因。
报错发生在代码行:_client.SendEmailAsync(emailMessage).Result
相关代码:
public EmailResponse Send(EmailContract contract) { var emailMessage = new SendGridMessage() { From = new EmailAddress(contract.FromEmailAddress, contract.Alias), Subject = contract.Subject, HtmlContent = contract.Body, PlainTextContent = contract.Body }; emailMessage.AddTo(new EmailAddress(contract.ToEmailAddress)); if (!string.IsNullOrWhiteSpace(contract.BccEmailAddress)) { emailMessage.AddBcc(new EmailAddress(contract.BccEmailAddress)); } if (!string.IsNullOrWhiteSpace(contract.CcEmailAddress)) { emailMessage.AddCc(new EmailAddress(contract.CcEmailAddress)); } return ProcessResponse(_client.SendEmailAsync(emailMessage).Result); }
可能的原因及解决方法
1. 同步阻塞调用引发的问题
代码中使用.Result同步等待异步方法完成,这种方式在ASP.NET等上下文环境中可能导致死锁,也会干扰HttpClient连接池的正常管理,进而引发连接异常。
解决方法:将方法改为异步实现,全程使用await替代.Result:
public async Task<EmailResponse> SendAsync(EmailContract contract) { var emailMessage = new SendGridMessage() { From = new EmailAddress(contract.FromEmailAddress, contract.Alias), Subject = contract.Subject, HtmlContent = contract.Body, PlainTextContent = contract.Body }; emailMessage.AddTo(new EmailAddress(contract.ToEmailAddress)); if (!string.IsNullOrWhiteSpace(contract.BccEmailAddress)) { emailMessage.AddBcc(new EmailAddress(contract.BccEmailAddress)); } if (!string.IsNullOrWhiteSpace(contract.CcEmailAddress)) { emailMessage.AddCc(new EmailAddress(contract.CcEmailAddress)); } var response = await _client.SendEmailAsync(emailMessage); return ProcessResponse(response); }
调用该方法的上层代码也需同步改为异步调用,保持异步流程一致性。
2. TLS版本不兼容
测试环境服务器可能默认使用TLS 1.0/1.1等旧版本协议,而SendGrid要求使用TLS 1.2或更高版本,导致握手失败被远程主机关闭连接。
解决方法:在项目启动阶段强制设置TLS版本:
// 可添加在Program.cs或Startup.cs的初始化代码中 System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls13;
3. SendGrid客户端实例管理不当
如果_client是每次请求都创建新实例,会导致连接资源耗尽;或者实例生命周期过长引发连接池异常。另一个测试站点可能正确复用了单例实例。
解决方法:将SendGrid客户端注册为单例模式(以依赖注入为例):
// 在Program.cs中添加注册代码 builder.Services.AddSingleton<ISendGridClient>(sp => new SendGridClient(builder.Configuration["SendGrid:ApiKey"]));
4. 项目网络配置差异
当前项目可能配置了代理,或者测试环境的防火墙、出站规则对该项目有特殊限制,而另一个站点未受影响。
解决方法:检查项目配置文件(如appsettings.json)的代理设置,确保测试环境允许项目访问SendGrid的API地址https://api.sendgrid.com。
内容的提问来源于stack exchange,提问作者Alex Becker

