提问人:Colin Roe 提问时间:1/11/2023 最后编辑:Colin Roe 更新时间:2/10/2023 访问量:656
.Net Framework - HttpClient SendAsync 错误 - 无法从传输连接读取数据:连接已关闭
.Net Framework - HttpClient SendAsync Error - Unable to read data from the transfer connection: the connection has been closed
问:
我正在尝试发送 post 请求,但在调试时收到 AggregateException:
“将内容复制到流时出错。” “无法从传输连接读取数据:连接已关闭。将内容复制到流时出错。 - 这可能意味着什么?
在没有调试的情况下,我收到错误“HttpClientHandler 中的异常 - 请求已中止:无法创建安全的 SSL/TLS 通道...”。
我正在使用 IHttpClientFactory,协议设置为 Tls12,这是我使用的正确协议。
使用 postman,我可以使用我的客户端证书成功发送 post 请求。
如果有人能指出我的代码中的缺陷和任何可能的想法以纠正问题,我将不胜感激。 如果我需要更清楚或发布更多代码,请告诉我。 谢谢!
/// Payment.cs
private async Task<HttpResponseMessage> CreatePurchase()
{
PurchaseService purchaseService = new PurchaseService(context, repository, pm.CategoryCode);
var httpResponseMessage = await purchaseService .CreatePurchaseRequest(context);
return httpResponseMessage;
}
public async Task<HttpResponseMessage> CreatePurchaseRequest(Context context)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
endPoint = repository[$"PurchaseService/Endpoints/CreateSettlementOnlyPurchase"];
// Configure httpClient with IHttpClientFactory
HttpClientFactoryProvider httpClientFactoryProvider = new HttpClientFactoryProvider(context.Merchant.Test);
// Get client from ServiceCollection
var httpClient = httpClientFactoryProvider.GetClient("Purchase");
// Create settlePurchaseRequest object
SettlePurchaseRequest settlePurchaseRequest = CreateSettlePurchase(context);
// Serialize object into JSON
var purchaseRequest = settlePurchaseRequest.ToJson();
// Create digest
var payloadDigest = purchaseRequest != null ? Digest(purchaseRequest) : null;
Dictionary<string, string> signHeaderInfo = CreateSignHeadersInfo(HeaderDateName, now, "POST", targetURL + endPoint, payloadDigest);
// Wrap JSON inside a StringContent object
var content = new StringContent(purchaseRequest, Encoding.UTF8, "application/json");
// Post to the endpoint
var requestMessage = new HttpRequestMessage(HttpMethod.Post, endPoint);
requestMessage.Content = content;
requestMessage.Headers.Add(HeaderDateName, now);
var guid = Guid.NewGuid().ToString();
requestMessage.Headers.Add("X-Request-ID", guid);
var signature = CreateSignature(signHeaderInfo);
requestMessage.Headers.Add("Signature", signature);
requestMessage.Headers.Add("Digest", payloadDigest);
using (HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead))
{
// Process response
httpResponseMessage.EnsureSuccessStatusCode();
var jsonString = httpResponseMessage.Content.ReadAsStringAsync().Result;
if (httpResponseMessage.IsSuccessStatusCode)
{
return httpResponseMessage;
}
else
{
return httpResponseMessage; // Todo: Refactor
}
}
}
private IHttpClientFactory HttpClientFactory()
{
if (_httpClientFactory != null)
{
return _httpClientFactory;
}
#region DI Service
var serviceCollection = new ServiceCollection();
#region Create Settlement Only Purchase
serviceCollection.AddHttpClient("Purchase", client =>
{
client.BaseAddress = new Uri(ApiURL);
client.DefaultRequestHeaders.Add("Api-Key", PurchaseApiKey);
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
client.DefaultRequestHeaders.Add("Keep-Alive", "3600");
client.DefaultRequestHeaders.Add("Host", hostName);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(GetCertificateBySerialNumber());
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
//handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
return handler;
});
#endregion
var serviceProvider = serviceCollection.BuildServiceProvider();
_httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
return _httpClientFactory;
#endregion
public HttpClient GetClient(string clientName)
{
return HttpClientFactory().CreateClient(clientName);
}
使用调试调用堆栈
System.Net.Http.HttpRequestException: 'Fehler beim Kopieren von Inhalt in einen Stream.'
Inner Exception: IOException: Von der Übertragungsverbindung können keine Daten gelesen werden: Die Verbindung wurde geschlossen.
StackTrace: at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
调用堆栈而不调试
12.01.2023 14:25:04:5738|INFO|Service|23192|0| * ITAD-199497 * Exception Caught!
12.01.2023 14:25:04:5749|ERROR|Service|23192|Message :{0} | * ITAD-199497 * Fehler beim Senden der Anforderung.
12.01.2023 14:25:04:5749|INFO|TXMS.PaymentCapture|18768|0| * ITAD-199497 * Caught aggregate exception-Task.Wait behavior
12.01.2023 14:25:04:5749|ERROR|TXMS.PaymentCapture|18768|0| * ITAD-199497 * PayID: 95052fe0a86246569c23976899a20ced. Die Anfrage wurde abgebrochen: Es konnte kein geschützter SSL/TLS-Kanal erstellt werden..
12.01.2023 14:25:04:5749|VERBOSE|TXMS.PaymentCapture|18768|0| * ITAD-199497 * bei System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
bei System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
12.01.2023 14:25:04:5758|ERROR|ProcessList|18768|0| * ITAD-199497 * Common.TXMSException: Unhandled exception when calling the API.
网络日志
System.Net.Http Error: 0 : [24128] HttpClient#56105527::SendAsync() - Fehler beim Senden von HttpRequestMessage#10319855. System.Net.Http.HttpRequestException: Fehler beim Kopieren von Inhalt in einen Stream. ---> System.IO.IOException: Von der Übertragungsverbindung können keine Daten gelesen werden: Die Verbindung wurde geschlossen.
bei System.Net.ConnectStream.EndWrite(IAsyncResult asyncResult)
bei System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- Ende der internen Ausnahmestapelüberwachung ---
bei System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
bei Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.<SendAsync>d__5.MoveNext()
--- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
bei System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
bei Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<SendAsync>d__5.MoveNext()
ProcessId=4124
DateTime=2023-01-12T13:40:20.6476208Z
更新:禁用“仅我的代码”并启用所有“例外设置”后,System.Security.Authentication.AuthenticationException:收到的消息意外或格式错误“。此问题的一般解决方案是检查是否使用了正确/最新的 TLS 版本,并检查密码套件。 根据我在 Wireshark 中捕获的内容,我认为没有 TLS 和密码问题。使用 TLS1.2 和 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。
答:
您正在从语句内部返回一次性对象。所以它在返回之前被处理掉了。using
using (HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead))
{
// ...
return httpResponseMessage;
}
您需要从作用域内的响应消息中读取所需的任何内容,或者删除语句并在完全处理后将消息释放到调用代码中。using
using
评论
Exception settings
Enable just my code
我理解您的困惑,看到在 HttpResponseMessage 上实现的接口,但我认为您不需要自己处理它。查看此处的类示例用法,您可以看到您可以简单地使用它,并退出该方法,而无需在语句中调用或保留它。你能试试看它是否有效吗?IDisposable
Dispose
using
此外,我对您实现该模式的方式感到有些困惑。依赖注入不适合您吗?如果是这样,我建议遵循本文中提到的准则,并将 注入到需要 .IHttpClientFactory
IHttpClientFactory
HttpClient
评论
我问题的解决方案在于我的 Windows 注册表编辑器。
让我想到这一点的是使用 curl 命令。我提供了 pem 格式的证书和密钥以及帖子请求的完整 URL。
curl --verbose --cert fullchain.pem --key privKey.pem {RequestUri}
我收到的错误消息的一部分是:
* schannel: disabled automatic use of client certificate
快速的谷歌找到了以下关于 SChannel 配置的文档: https://learn.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings?tabs=diffie-hellman#messaging--fragment-parsing
注册表路径:HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Messaging
若要指定 TLS 客户端将接受的分段 TLS 握手消息的最大允许大小,请创建一个 MessageLimitClient 条目。创建条目后,将 DWORD 值更改为所需的位长度。如果未配置,则默认值将为 0x8000 字节。
添加 MessageLimitClient 后,我能够收到来自我的 Post 请求的响应。
评论