72 votes

C# HttpClient Une connexion existante a été fermée de force par l'hôte distant.

Je travaille sur une intégration avec Alternative Payments en utilisant leur intégration de pages hébergées . Leur SDK C# n'a pas cette intégration disponible pour le moment, mais comme vous pouvez le voir c'est assez simple et j'ai fait une petite classe pour envoyer la requête post et obtenir la réponse JSON.

J'ai testé l'objet json que j'envoie sur PostMan et cURL et les deux fonctionnent, ainsi que l'en-tête d'authentification, donc je pense qu'ils ne sont pas le problème. Voici le constructeur de ma classe :

public AlternativePaymentsCli(string apiSecretKey)
{
    this._apiSecretKey = apiSecretKey;

    _httpClient = new HttpClient();
    _httpClient.DefaultRequestHeaders.Accept
        .Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var authInfo = _apiSecretKey;
    authInfo = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:", _apiSecretKey)));

    // The two line below because I saw in an answer on stackoverflow.
    _httpClient.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); 
    _httpClient.DefaultRequestHeaders.Add("Keep-Alive", "3600");

    _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Anything.com custom client v1.0");
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);

}

Et la méthode où j'enregistre les données :

public string CreateHostedPageTransaction(HostedPageRequest req) 
{
    var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

    // I send this same json content on PostMan and it works. The json is not the problem
    var content = new StringContent(JsonConvert.SerializeObject(req, settings), Encoding.UTF8, "application/json");
    var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result;
    var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.IsSuccessStatusCode)
        return responseText;

    return "";
}

Puis j'obtiens cette erreur : An existing connection was forcibly closed by the remote host à la ligne PostAsync. Voici les détails de l'erreur :

[SocketException (0x2746): An existing connection was forcibly closed by the remote host]
   System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) +8192811
   System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) +47

[IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.]
   System.Net.TlsStream.EndWrite(IAsyncResult asyncResult) +294
   System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar) +149

[WebException: The underlying connection was closed: An unexpected error occurred on a send.]
   System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) +324
   System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) +137

[HttpRequestException: An error occurred while sending the request.]

J'utilise C# 4.5, Asp.Net MVC. J'ai lu des réponses pour la même erreur et aucune d'entre elles n'a résolu mon problème jusqu'à présent. Que me manque-t-il dans ce code ?

Merci pour toute aide

0 votes

Que voyez-vous, le cas échéant, dans la réponse de Fiddler ?

12 votes

Juste un bon conseil, en utilisant var pour tout fera que vos collègues vous détesteront. N'utilisez var lorsque le type est apparent (c'est-à-dire var date = new DateTime(); ) son très clairement un DateTime . Cependant var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result; est pas clair parce que .Result est une propriété et non un type implicite.

19 votes

@maccettura au contraire, en utilisant var rend le code beaucoup plus propre. Il n'y a pas d'ambiguïté sur le type, sauf si vous écrivez des méthodes très longues, auquel cas vous devez vraiment, vraiment les séparer. Les collègues de travail vous détesteront si vous écrivez des méthodes si longues qu'ils ne peuvent pas voir quels sont les types.

196voto

Paul Pearce Points 1085

Je ne vois pas dans votre exemple de code où vous définissez la valeur de _baseUrl, mais je suppose que cela est fait quelque part. Je suppose également que, puisque cela concerne les paiements, l'URL est HTTPS. Si l'hôte distant a désactivé TLS 1.0 et que votre connexion entre en tant que TLS 1.0, cela pourrait provoquer ce comportement. Je sais que la prise en charge de TLS 1.0/1.1/1.2 est activée par défaut dans C# 4.6, mais je pense que C# 4.6 utilise toujours par défaut uniquement SSL3/TLS 1.0, même si TLS 1.1 et 1.2 sont pris en charge. Si cela est la cause du problème, vous pouvez ajouter manuellement TLS 1.1 et 1.2 aux valeurs activées en utilisant le code suivant.

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

1 votes

Merci beaucoup Monsieur, vous avez résolu mon problème. Localement, je lance un faux HTTPS, chrome m'avertit que le site n'est pas sûr etc ça doit être ça.

2 votes

Il n'est pas nécessaire de modifier le code pour activer cette fonction pour les applications qui sont actuellement exécutées en prod : docs.microsoft.com/fr/us/officeonlineserver/

1 votes

Oui, est-ce que cela n'est nécessaire que dans le domaine de l'application (en utilisant un rôle de travailleur) ou dois-je le définir pour chaque requête HttpClient ?

21voto

Tim Kempster Points 71

Si vous utilisez .Net 4.0, SecurityProtocolType.Tls11 et SecurityProtocolType.Tls2 ne sont pas définis. Vous pouvez donc utiliser la valeur codée en dur ci-dessous.

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

13voto

d_f Points 490

Il est possible de résoudre le problème sans aucune modification du code, comme décrit dans cette excellente réponse à une question similaire :

Recibler le projet web sur .Net 4.6+ puis mettez à jour le web.config comme suit :

<system.web>
  <compilation targetFramework="4.6" /> 
  <httpRuntime targetFramework="4.6" /> 
</system.web>

1voto

sao Points 11

Cela a fonctionné pour moi, la première ligne assure les protocoles ssl3 et TLS1.2 et la deuxième ligne ignore toute erreur potentielle de certificat (ignorer et continuer - comme les certificats expirés) :

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=  (sender, certificate, chain, sslPolicyErrors) => true;

1 votes

Bonjour et bienvenue dans la communauté. Essayez d'utiliser les outils de formatage dans l'éditeur de texte pour améliorer votre réponse et la rendre plus facile à lire. Le formateur de code est particulièrement utile et il fera des merveilles.

1voto

Martijn Vink Points 1

Pour moi, cette erreur a été causée par l'oubli de la configuration du serveur proxy. En utilisant le code suivant pour construire le HttpClient Je l'ai résolu pour mon application .NET 4.7.2 :

var httpClientHandler = new HttpClientHandler { Proxy = WebRequest.GetSystemWebProxy() };
var httpClient = new HttpClient(httpClientHandler);

J'espère que cela aidera quelqu'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X