Подтвердить что ты не робот

System.Net.Http.HttpRequestException Ошибка при копировании содержимого в поток

Я использую класс HttpClient в .NET Framework 4.5.2. Я делаю PostAsync для стороннего веб-сервиса. В 80% случаев это сообщение работает, в 20% случаев наш ответ прерван. В этой ситуации мы получаем следующее исключение:

System.Net.Http.HttpRequestException: Ошибка при копировании содержимого в поток. --- > System.IO.IOException: не удается прочитать данные из транспортное соединение: существующее соединение было принудительно закрыто удаленный хост. --- > System.Net.Sockets.SocketException: существующее соединение было принудительно закрыто удаленным хостом в System.Net.Sockets.NetworkStream.BeginRead(Байт [] buffer, Int32 смещение, размер Int32, обратный вызов AsyncCallback, состояние объекта) --- Конец внутренней трассировки стека исключений - at System.Net.Sockets.NetworkStream.BeginRead(Байт [] buffer, Int32 смещение, размер Int32, обратный вызов AsyncCallback, состояние объекта) на System.Net.FixedSizeReader.StartReading() в System.Net.Security._SslStream.StartFrameHeader(буфер Byte [], Int32 смещение, Int32-счет, AsyncProtocolRequest asyncRequest) в System.Net.Security._SslStream.StartReading(буфер Byte [], Int32 смещение, Int32-счет, AsyncProtocolRequest asyncRequest) в System.Net.Security._SslStream.ProcessRead(байт [] буфер, Int32 смещение, Int32-счет, AsyncProtocolRequest asyncRequest) в System.Net.TlsStream.BeginRead(буфер Byte [], смещение Int32, Int32 размер, AsyncCallback asyncCallback, Object asyncState) при System.Net.ConnectStream.BeginReadWithoutValidation(Байт [] buffer, Смещение Int32, размер Int32, обратный вызов AsyncCallback, состояние объекта) при System.Net.ConnectStream.BeginRead(буфер байта [], смещение Int32, Int32 размер, обратный вызов AsyncCallback, состояние объекта) в System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(байт [] буфер, смещение Int32, количество Int32, обратный вызов AsyncCallback, объект state) в System.Net.Http.StreamToStreamCopy.StartRead()

Последующий идентичный запрос завершается успешно. Это не запрос, который мы можем повторить, поскольку бизнес уже размещен. Поэтому он оставляет нас в неловкой ситуации.

Это мой код:

using (var httpClient = new HttpClient())
{
    httpClient.DefaultRequestHeaders.Authorization = authorizationHeader;
    HttpContent httpContent = new StringContent(someXml);

    //Exception occurs on next line...
    var response = await httpClient.PostAsync("https://thirdpartyendpoint", httpContent);
    var responseXml = await response.Content.ReadAsStringAsync();  
    //convert to Dto              
}

Служба сторонних разработчиков успешно сохраняет запись в своей базе данных и не видит никаких очевидных исключений в конце. Они отметили, что запросы на отказ обычно занимают больше времени (около 18-30 секунд) для записи в базу данных, чем успешные запросы.

Спасибо за вашу помощь

4b9b3361

Ответ 1

мы решили эту проблему с двумя изменениями кода:

  • Утилизируйте httpResponseMessage и просто работайте с простым DTO

     using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage))
        {
            return await CreateDto(httpResponseMessage);
        }
    
  • Снизьте версию HTTP до версии 1.0

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url))
    {
        Version = HttpVersion.Version10,
        Content = httpContent
    };
    
    await client.SendAsync(httpRequestMessage);
    

который влияет на добавление этого заголовка Http

    Connection: close 

а не

    Connection: keep-alive

Ответ 2

У меня была похожая проблема с использованием общего HttpClient, подключающегося к серверу для вызовов REST. В результате возникла проблема с несоответствием тайм-аута KeepAlive на клиенте и сервере. Тайм- аут на стороне клиента задается параметром MaxServicePointIdleTime в ServicePointManager и по умолчанию равен 100 с. Время ожидания на стороне сервера было установлено на нашем сервере более коротким.

Сокращение времени ожидания на сервере по сравнению с клиентом привело к тому, что сервер время от времени закрывал соединение, когда клиент пытался подключиться. Это привело к сообщенному исключению.

Обратите внимание, что я в конечном итоге обнаружил проблему, потому что я также получил это исключение при тех же условиях:

System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

Ответ 3

У меня была такая же ошибка (ошибка при копировании контента в поток) с методом HTTPClient PutAsync():

using (StreamContent content = new StreamContent(stream))
{
    HttpResponseMessage response = await client.PutAsync(url, content))
}

Вам нужно указать флаг HttpCompletionOption.ResponseHeadersRead, который недоступен в PutAsync, поэтому я переключился на SendAsync:

using (StreamContent content = new StreamContent(stream))
{
    var httpRequest = new HttpRequestMessage(HttpMethod.Put, url);
    httpRequest.Content = content;

    HttpResponseMessage response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
}