C# HttpClient Koneksi yang ada ditutup paksa oleh host jarak jauh

Saya sedang mengerjakan integrasi dengan Pembayaran Alternatif menggunakan integrasi laman yang dihosting. C# SDK mereka tidak memiliki integrasi ini saat ini, tetapi seperti yang Anda lihat, ini cukup sederhana dan saya membuat kelas kecil untuk mengirim permintaan posting dan mendapatkan respons JSON.

Saya menguji objek json yang saya kirim di PostMan dan cURL dan keduanya berfungsi, juga header otentikasi, jadi menurut saya itu bukan masalahnya. Inilah konstruktor kelas saya:

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);

}

Dan metode saya memposting data:

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 "";
}

Lalu saya mendapatkan kesalahan ini: An existing connection was forcibly closed by the remote host, di baris PostAsync. Ini rincian kesalahannya:

[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.]

Saya menggunakan C# 4.5, Asp.Net MVC. Saya telah membaca jawaban untuk kesalahan yang sama dan sejauh ini tidak ada satupun yang menyelesaikan masalah saya. Apa yang saya lewatkan dalam kode ini?

Terima kasih atas bantuannya


person André Luiz    schedule 14.09.2017    source sumber
comment
Bagaimana jika ada yang Anda lihat dalam respons di Fiddler?   -  person DiskJunky    schedule 14.09.2017
comment
Sekadar tip yang bagus, menggunakan var untuk segala hal akan membuat rekan kerja Anda membenci Anda. Hanya gunakan var jika jenisnya jelas (misalnya var date = new DateTime();) sangat jelas merupakan DateTime. Namun var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result; tidak jelas karena .Result adalah properti dan tidak tersirat jenisnya.   -  person maccettura    schedule 14.09.2017
comment
@macettura sebaliknya, menggunakan var membuat kode lebih bersih. Tidak ada ambiguitas tentang jenisnya kecuali Anda menulis metode yang sangat panjang, dalam hal ini Anda harus benar-benar memisahkannya. Rekan kerja akan membenci Anda jika Anda menulis metode yang terlalu panjang sehingga mereka tidak dapat melihat jenisnya   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos var hanya membuat kode lebih bersih ketika tipenya tersirat, menggunakan var di mana pun (bahkan dalam kasus di mana tipenya tidak tersirat) hanyalah pemrograman yang buruk.   -  person maccettura    schedule 14.09.2017
comment
@macccettura untuk .Result, rekan kerja akan membenci siapa pun yang memblokir panggilan asinkron dengan .Wait() atau .Result. Tipe pengembaliannya tetap diketahui. Ini adalah HttpResponseMessage. Melihat namanya tidak akan membantu Anda karena Anda tetap harus menggunakan intellisense untuk menemukan metode mana yang akan digunakan   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@macettura yang, misalnya, menjadi alasan mengapa bahasa fungsional menggunakan inferensi tipe alih-alih menentukan tipe secara eksplisit. Anda mungkin belum familiar dengan inferensi tipe. Itu tidak menjadikannya pemrograman yang buruk. Di sisi lain, panggilan ke .Result akan menimbulkan kekhawatiran siapa pun   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos apakah benar-benar diketahui? Mengabaikan pengalaman sebelumnya di perpustakaan .NET mana pun, apa cara termudah bagi saya untuk mengetahui jenisnya? (Dengan mendeklarasikannya jika tidak tersirat secara berlebihan.   -  person maccettura    schedule 14.09.2017
comment
@PanagiotisKanavos dan ya, saya setuju bahwa OP tidak boleh memblokir panggilan async (jika belum tahu, kode yang saya posting langsung dari pertanyaan OP dan bukan pertanyaan saya sendiri).   -  person maccettura    schedule 14.09.2017
comment
Benar-benar mati. Saya tidak tahu IDe mana yang Anda gunakan, saya menggunakan Visual Studio dan ketika Anda mengarahkan variabel, ia memberi tahu Anda tipenya. Terima kasih   -  person André Luiz    schedule 14.09.2017
comment
Kemungkinan duplikat Koneksi yang ada ditutup paksa oleh remote tuan rumah   -  person Liam    schedule 15.02.2019


Jawaban (5)


Saya tidak melihat dalam contoh kode Anda di mana Anda menetapkan nilai _baseUrl, tapi saya berasumsi itu sedang dilakukan di suatu tempat. Saya juga berasumsi karena ini terkait dengan pembayaran, URL-nya adalah HTTPS. Jika host jarak jauh telah menonaktifkan TLS 1.0 dan koneksi Anda masuk sebagai TLS 1.0, hal ini dapat menyebabkan perilaku tersebut. Saya tahu C# 4.6 memiliki dukungan TLS 1.0/1.1/1.2 yang diaktifkan secara default, tapi menurut saya C# 4.6 masih default hanya SSL3/TLS 1.0 meskipun TLS 1.1 dan 1.2 didukung. Jika ini penyebab masalahnya, Anda dapat menambahkan TLS 1.1 dan 1.2 secara manual ke nilai yang diaktifkan menggunakan kode berikut.

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
person Paul Pearce    schedule 14.09.2017
comment
terima kasih banyak Pak, Anda memecahkan masalah saya. Secara lokal saya menjalankan HTTPS palsu, chrome memperingatkan saya bahwa situs tersebut tidak aman dll.. pasti itu. - person André Luiz; 14.09.2017
comment
Anda tidak perlu mengubah kode untuk mengaktifkan ini untuk aplikasi yang sedang berjalan di prod: docs.microsoft.com/en-us/officeonlineserver/ - person Sean Anderson; 25.01.2018
comment
ya, apakah ini hanya diperlukan dalam domain aplikasi (menggunakan peran pekerja) atau apakah saya perlu mengatur ini sesuai permintaan HttpClient? - person Nathan Tregillus; 17.06.2018
comment
@Ruchira dan NathanTregillus Anda dapat mengaturnya per permintaan (menambahkan kode sebelum Anda mengirim permintaan), atau Anda dapat mengaturnya satu kali untuk seluruh aplikasi dengan mengaturnya Application_Start() di Global.asax.cs - person Paul Pearce; 18.06.2018
comment
Terima kasih @PaulPearce. Saya akhirnya menambahkan ini ke Global.asax. Digunakan menggunakan System.Net; tambahkan bagian atas untuk mendukung ServicePointManager dan SecurityProtocolType. - person Ruchira; 20.06.2018
comment
Terima kasih. Masalah saya adalah Server 2012. Saya memindahkan API non SSL lama ke Azure Functions yang diberi SSL dan ini mulai terjadi. Solusi Anda memperbaikinya. Terima kasih banyak! - person Piotr Kula; 05.12.2018
comment
Berjuang selama 3 hari dengan pengecualian ini dan ini memperbaikinya! - person Rush Frisby; 24.06.2019
comment
Saya menambahkan baris kode ini. Tapi itu tidak berhasil untuk saya. Saya menggunakan .NET Framework 4.6.1. Saya dapat membuat panggilan api yang sama di Postman atau skrip Powershell tanpa kesalahan ini. Namun ketika saya memanggil api menggunakan HttpClient, saya mendapatkan kesalahan yang sama. - person user3293338; 08.08.2019
comment
@ user3293338 - apakah Anda menambahkan kode ke klien panggilan atau ke layanan penerima? Ini harus ditambahkan ke klien panggilan untuk memungkinkannya terhubung menggunakan versi TLS yang lebih aman. Jika Anda dapat menyertakan contoh kode Anda, itu akan membantu dalam mengidentifikasi masalahnya. - person Paul Pearce; 08.08.2019
comment
Saya menambahkan kode sebelum melakukan panggilan API. Kodenya terlihat sebagai berikut: var stream = new FileStream(filePath, FileMode.Open); var konten = StreamContent baru(aliran); klien.DefaultRequestHeaders.Add(X-Atlassian-Token, nocheck); System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var respon = klien.PostAsync(apiUrl, konten).Hasil; - person user3293338; 08.08.2019
comment
Jawaban ini sangat bagus, juga layak untuk diupdate menggunakan SSL3 juga: ...| SecurityProtocolType.Ssl3 - person Sxntk; 19.03.2020
comment
Terima kasih, jawaban Anda menyelesaikan masalah saya. Itu terjadi ketika layanan saya menggunakan Azure dan klien tidak mengaktifkannya secara default - person Pham Lai; 23.06.2020

Jika Anda menggunakan .Net 4.0 maka SecurityProtocolType.Tls11 dan SecurityProtocolType.Tls2 tidak ditentukan sehingga Anda dapat menggunakan nilai hard code di bawah ini.

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

person Tim Kempster    schedule 08.01.2018

Masalah ini dapat diselesaikan tanpa perubahan apa pun pada kode, seperti yang dijelaskan dalam jawaban bagus ini untuk pertanyaan serupa:

Targetkan ulang proyek web ke .Net 4.6+, lalu perbarui web.config sebagai berikut:

<system.web>
  <compilation targetFramework="4.6" /> 
  <httpRuntime targetFramework="4.6" /> 
</system.web>
person d_f    schedule 03.09.2019

Ini berhasil bagi saya, baris pertama memastikan protokol ssl3 dan TLS1.2, dan baris kedua mengabaikan potensi kesalahan sertifikat (abaikan dan lanjutkan - seperti sertifikat yang kedaluwarsa.):

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=  (sender, certificate, chain, sslPolicyErrors) => true;
person sao    schedule 24.02.2020
comment
Halo dan selamat datang di komunitas, coba gunakan alat pemformatan di editor teks untuk membuat jawaban Anda terlihat lebih baik dan lebih mudah dibaca. Pemformat Kode sangat berguna dan akan menghasilkan keajaiban. - person Fabio Lolli; 24.02.2020

Bagi saya kesalahan ini disebabkan oleh lupa mengkonfigurasi server proxy. Menggunakan kode berikut untuk membuat HttpClient menyelesaikannya untuk aplikasi .NET 4.7.2 saya:

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

Semoga ini bisa membantu seseorang.

person Martijn Vink    schedule 09.04.2020