C # HttpClient การเชื่อมต่อที่มีอยู่ถูกบังคับให้ปิดโดยโฮสต์ระยะไกล

ฉันกำลังดำเนินการบูรณาการกับ Alternative Payments โดยใช้การรวมหน้าที่โฮสต์. C# SDK ของพวกเขาไม่มีการบูรณาการนี้ในขณะนี้ แต่อย่างที่คุณเห็นว่ามันค่อนข้างง่าย และฉันได้จัดชั้นเรียนขนาดเล็กเพื่อส่งคำขอโพสต์และรับการตอบกลับ JSON

ฉันทดสอบออบเจ็กต์ json ที่ฉันส่งบน PostMan และ cURL และใช้งานได้ทั้งคู่ รวมถึงส่วนหัวการตรวจสอบสิทธิ์ด้วย ดังนั้นฉันคิดว่ามันไม่ใช่ปัญหา นี่คือตัวสร้างชั้นเรียนของฉัน:

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

}

และวิธีการที่ฉันโพสต์ข้อมูล:

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

จากนั้นฉันได้รับข้อผิดพลาดนี้: An existing connection was forcibly closed by the remote host ที่บรรทัด PostAsync นี่คือรายละเอียดข้อผิดพลาด:

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

ฉันใช้ C# 4.5, Asp.Net MVC ฉันได้อ่านคำตอบสำหรับข้อผิดพลาดเดียวกันนี้แล้ว แต่ยังไม่มีใครแก้ไขปัญหาของฉันได้ ฉันพลาดอะไรไปในรหัสนี้?

ขอบคุณสำหรับความช่วยเหลือใด ๆ


person André Luiz    schedule 14.09.2017    source แหล่งที่มา
comment
แล้วคุณเห็นอะไรในการตอบกลับใน Fiddler ล่ะ?   -  person DiskJunky    schedule 14.09.2017
comment
เคล็ดลับดีๆ การใช้ var กับทุกสิ่งจะทำให้เพื่อนร่วมงานเกลียดคุณ ใช้ var เมื่อประเภทปรากฏชัดเจนเท่านั้น (เช่น var date = new DateTime();) มาก ชัดเจนถึง DateTime อย่างไรก็ตาม var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result; ไม่ชัดเจน เนื่องจาก .Result เป็นคุณสมบัติและไม่ได้บอกเป็นนัยว่าเป็นประเภทใด   -  person maccettura    schedule 14.09.2017
comment
@maccettura ตรงกันข้ามการใช้ var ทำให้โค้ดสะอาดขึ้นมาก ไม่มีความคลุมเครือเกี่ยวกับประเภทนั้นเว้นแต่คุณจะเขียนวิธีการที่ยาวมาก ซึ่งในกรณีนี้คุณควรแยกมันออกจากกันจริงๆ เพื่อนร่วมงานจะเกลียดคุณถ้าคุณเขียนวิธีการที่ยาวจนพวกเขาไม่รู้ว่าประเภทไหน   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos var ทำความสะอาดโค้ดเฉพาะเมื่อ ประเภทนั้นมีความหมายโดยนัย การใช้ var ทุกที่ (แม้ในกรณีที่ประเภทไม่ได้บอกเป็นนัย) เป็นเพียง การเขียนโปรแกรมที่ไม่ดี   -  person maccettura    schedule 14.09.2017
comment
@maccettura สำหรับ .Result เพื่อนร่วมงานจะเกลียดใครก็ตามที่บล็อกการโทรแบบอะซิงโครนัสด้วย .Wait() หรือ .Result ทราบประเภทการคืนสินค้าอยู่แล้ว มันคือ HttpResponseMessage การเห็นชื่อนั้นไม่ได้ช่วยอะไร เพราะคุณจะต้องใช้ Intellisense อยู่ดีเพื่อดูว่าจะใช้วิธีไหน   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@maccettura ซึ่งยกตัวอย่างว่าทำไมภาษาที่ใช้งานได้จึงใช้การอนุมานประเภทแทนที่จะระบุประเภทอย่างชัดเจน คุณอาจไม่ คุ้นเคย กับการอนุมานประเภท นั่นไม่ได้ทำให้การเขียนโปรแกรมไม่ดี ในทางกลับกัน การโทรไปที่ .Result น่าจะทำให้เกิดช่องโหว่ของใครก็ตาม   -  person Panagiotis Kanavos    schedule 14.09.2017
comment
@PanagiotisKanavos มัน จริงๆ เป็นที่รู้จักเหรอ? หากเพิกเฉยต่อประสบการณ์ก่อนหน้านี้ในไลบรารีของ .NET ใด ๆ วิธีที่ง่ายที่สุดสำหรับฉันในการทราบประเภทคืออะไร (โดยการประกาศในกรณีที่ ไม่ได้บอกเป็นนัยอย่างซ้ำซ้อน   -  person maccettura    schedule 14.09.2017
comment
@PanagiotisKanavos และใช่ ฉันยอมรับว่า OP ไม่ควรบล็อกการโทรแบบ async (หากยังไม่รู้ รหัสที่ฉันโพสต์นั้นตรงจากคำถามของ OP ไม่ใช่ของฉันเอง)   -  person maccettura    schedule 14.09.2017
comment
ปิดเลย ฉันไม่รู้ว่าคุณใช้ IDe ตัวไหน ฉันใช้ Visual Studio และเมื่อคุณวางเมาส์เหนือตัวแปร มันจะบอกประเภทให้คุณทราบ ขอบคุณ   -  person André Luiz    schedule 14.09.2017


คำตอบ (5)


ฉันไม่เห็นในตัวอย่างโค้ดของคุณที่คุณกำลังตั้งค่าเป็น _baseUrl แต่ฉันคิดว่านั่นกำลังดำเนินการอยู่ที่ไหนสักแห่ง ฉันยังสันนิษฐานว่าเนื่องจากสิ่งนี้เกี่ยวข้องกับการชำระเงิน URL จึงเป็น HTTPS หากโฮสต์ระยะไกลปิดใช้งาน TLS 1.0 และการเชื่อมต่อของคุณเข้ามาเป็น TLS 1.0 ก็อาจทำให้เกิดลักษณะดังกล่าวได้ ฉันรู้ว่า C# 4.6 มีการเปิดใช้งานการสนับสนุน TLS 1.0/1.1/1.2 ตามค่าเริ่มต้น แต่ฉันคิดว่า C# 4.6 ยังคงมีค่าเริ่มต้นเป็น SSL3/TLS 1.0 เท่านั้น แม้ว่าจะรองรับ TLS 1.1 และ 1.2 ก็ตาม หากนี่คือสาเหตุของปัญหา คุณสามารถเพิ่ม TLS 1.1 และ 1.2 ให้กับค่าที่เปิดใช้งานได้ด้วยตนเองโดยใช้รหัสต่อไปนี้

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
person Paul Pearce    schedule 14.09.2017
comment
ขอบคุณมากครับ คุณแก้ไขปัญหาของผมได้แล้ว ในพื้นที่ฉันใช้ HTTPS ปลอม Chrome เตือนฉันว่าไซต์ไม่ปลอดภัย ฯลฯ ก็ต้องเป็นเช่นนั้น - person André Luiz; 14.09.2017
comment
คุณไม่จำเป็นต้องแก้ไขโค้ดเพื่อเปิดใช้งานสิ่งนี้สำหรับแอปพลิเคชันที่กำลังทำงานอยู่ในผลิตภัณฑ์: docs.microsoft.com/en-us/officeonlineserver/ - person Sean Anderson; 25.01.2018
comment
ใช่ สิ่งนี้จำเป็นเฉพาะภายในโดเมนแอป (ใช้ในบทบาทผู้ปฏิบัติงาน) หรือฉันต้องตั้งค่านี้ตามคำขอ HttpClient - person Nathan Tregillus; 17.06.2018
comment
@Ruchira และ NathanTregillus คุณสามารถตั้งค่าตามคำขอ (เพิ่มรหัสก่อนที่จะส่งคำขอของคุณ) หรือคุณสามารถตั้งค่าครั้งเดียวสำหรับแอปพลิเคชันทั้งหมดโดยการตั้งค่า Application_Start() ใน Global.asax.cs - person Paul Pearce; 18.06.2018
comment
ขอบคุณ @PaulPearce ฉันลงเอยด้วยการเพิ่มสิ่งนี้ลงใน Global.asax ใช้โดยใช้ System.Net; เพิ่มด้านบนเพื่อรองรับ ServicePointManager และ SecurityProtocolType - person Ruchira; 20.06.2018
comment
ขอบคุณ. ปัญหาของฉันคือเซิร์ฟเวอร์ 2012 ฉันย้าย API ที่ไม่ใช่ SSL เก่าไปยังฟังก์ชัน Azure ที่ใช้ SSL และสิ่งนี้เริ่มเกิดขึ้น โซลูชันของคุณแก้ไขแล้ว ขอบคุณมาก! - person Piotr Kula; 05.12.2018
comment
พยายามดิ้นรนเป็นเวลา 3 วันด้วยข้อยกเว้นนี้ และสิ่งนี้ก็แก้ไขได้! - person Rush Frisby; 24.06.2019
comment
ฉันเพิ่มโค้ดบรรทัดนี้ แต่มันไม่ทำงานสำหรับฉัน ฉันใช้ .NET Framework 4.6.1 ฉันสามารถทำการเรียก api เดียวกันในบุรุษไปรษณีย์หรือในสคริปต์ Powershell โดยไม่มีข้อผิดพลาดนี้ แต่เมื่อฉันเรียก api โดยใช้ HttpClient ฉันได้รับข้อผิดพลาดเดียวกัน - person user3293338; 08.08.2019
comment
@ user3293338 - คุณเพิ่มรหัสไปยังไคลเอนต์ที่โทรหรือบริการที่รับหรือไม่ ควรเพิ่มลงในไคลเอ็นต์การโทรเพื่อให้สามารถเชื่อมต่อโดยใช้เวอร์ชัน TLS ที่ปลอดภัยยิ่งขึ้น หากคุณสามารถใส่ตัวอย่างโค้ดของคุณได้ นั่นจะเป็นประโยชน์ในการระบุปัญหา - person Paul Pearce; 08.08.2019
comment
ฉันเพิ่มรหัสก่อนที่จะทำการเรียก API โค้ดมีลักษณะดังนี้: var stream = new FileStream(filePath, FileMode.Open); เนื้อหา var = StreamContent ใหม่ (สตรีม); client.DefaultRequestHeaders.Add (X-Atlassian-Token, nocheck); System.Net.ServicePointManager.SecurityProtocol | = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; การตอบสนอง var = client.PostAsync (apiUrl, เนื้อหา) ผลลัพธ์; - person user3293338; 08.08.2019
comment
คำตอบนี้ดีมาก นอกจากนี้ยังคุ้มค่ากับการอัปเดตเพื่อใช้ Ssl3 ด้วย: ...| ความปลอดภัย ProtocolType.Ssl3 - person Sxntk; 19.03.2020
comment
ขอบคุณคำตอบของคุณแก้ไขปัญหาของฉัน มันเกิดขึ้นเมื่อบริการของฉันที่ใช้ Azure และไคลเอนต์ไม่เปิดใช้งานตามค่าเริ่มต้น - person Pham Lai; 23.06.2020

หากคุณใช้ .Net 4.0 จะไม่มีการกำหนด SecurityProtocolType.Tls11 และ SecurityProtocolType.Tls2 ดังนั้น คุณสามารถใช้ค่าฮาร์ดโค้ดด้านล่างแทนได้

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

person Tim Kempster    schedule 08.01.2018

เป็นไปได้ที่จะแก้ไขปัญหาโดยไม่ต้องเปลี่ยนแปลงโค้ดตามที่อธิบายไว้ใน คำตอบที่ยอดเยี่ยมนี้สำหรับคำถามที่คล้ายกัน:

กำหนดเป้าหมายโครงการเว็บใหม่เป็น .Net 4.6+ จากนั้นอัปเดต web.config ดังต่อไปนี้:

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

สิ่งนี้ได้ผลสำหรับฉัน บรรทัดแรกรับประกันโปรโตคอล ssl3 และ TLS1.2 และบรรทัดที่สองละเว้นข้อผิดพลาดใบรับรองที่อาจเกิดขึ้น (ละเว้นและดำเนินการต่อ - เหมือนใบรับรองที่หมดอายุ):

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=  (sender, certificate, chain, sslPolicyErrors) => true;
person sao    schedule 24.02.2020
comment
สวัสดีและยินดีต้อนรับสู่ชุมชน ลองใช้เครื่องมือการจัดรูปแบบในโปรแกรมแก้ไขข้อความเพื่อทำให้คำตอบของคุณดูดีขึ้นและอ่านง่ายขึ้น ตัวจัดรูปแบบโค้ดมีประโยชน์อย่างยิ่งและจะทำงานได้อย่างมหัศจรรย์ - person Fabio Lolli; 24.02.2020

สำหรับฉันข้อผิดพลาดนี้เกิดจากการลืมกำหนดค่าพร็อกซีเซิร์ฟเวอร์ การใช้รหัสต่อไปนี้เพื่อสร้าง HttpClient แก้ไขได้สำหรับแอปพลิเคชัน .NET 4.7.2 ของฉัน:

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

หวังว่านี่จะช่วยใครซักคน

person Martijn Vink    schedule 09.04.2020