如何解决C#调用PUT接口时出现的‘underlying connection was closed’错误
我有以下JSON数据,正在通过PUT方法将其传递给API。该API在Postman中能正常返回响应,但使用下方C#代码调用时,出现错误提示“the underlying connection was closed”。请检查以下代码并告知如何解决此问题。
JSON数据
[ { "transaction": { "Version": "1.01", "TranDtls": { "TaxSch": "GST", "SupTyp": "EXPWOP", "RegRev": "N", "EcmGstin": null, "IgstOnIntra": null }, "DocDtls": { "Typ": "INV", "No": "G/E/20-21/0175", "Dt": "11/09/2020" }, "SellerDtls": { "Gstin": "29AAFCD5862R000", "LglNm": "K.H Exports India Private Limited", "TrdNm": "K.H Exports India Private Limited (Glove Division)", "Addr1": "142/,Trunk Road", "Addr2": "Perumugai", "Loc": "Via Vellore", "Pin": "560037", "Stcd": "29", "Ph": "04162253164", "Em": "edp.kharind@khindia.com" }, "BuyerDtls": { "Gstin": "URP", "LglNm": "H M FINANCE AB", "TrdNm": "H M FINANCE AB", "Pos": "96", "Addr1": "MASTER SAMUELSGATAN 46 A, 106 38 STOCKHOLM SWEDEN ", "Addr2": null, "Loc": "Sweden", "Pin": "999999", "Stcd": "96", "Ph": null, "Em": null }, "DispDtls": null, "ShipDtls": { "Gstin": "URP", "LglNm": "LOGIX FZCO", "TrdNm": "LOGIX FZCO", "Addr1": "PO BOX 261422 PLOT NO. S21515, SOUTH BLOCK JEBEL ALI FREE ZONE DUBAI UNITED ARAB EMIRATES ", "Addr2": null, "Loc": "United Arab Emirates", "Pin": "999999", "Stcd": "96" }, "ItemList": [ { "SlNo": "0001", "PrdDesc": "FINE LEATHER GLOVES MADE OUT OF SHEEP LEATHER FOR LADIES. ", "IsServc": "N", "HsnCd": "42032920", "Barcde": null, "Qty": 10.0, "FreeQty": 0.0, "Unit": "PR", "UnitPrice": 452.50, "TotAmt": 4525.00, "Discount": 0.0, "PreTaxVal": 4525.00, "AssAmt": 4525.00, "GstRt": 0.0, "IgstAmt": 0.0, "CgstAmt": 0.0, "SgstAmt": 0.0, "CesRt": 0.0, "CesAmt": 0.0, "CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0, "OthChrg": 0.0, "TotItemVal": 4525.00, "OrdLineRef": null, "OrgCntry": null, "PrdSlNo": null, "BchDtls": null, "AttribDtls": null } ], "ValDtls": { "AssVal": 4525.00, "CgstVal": 0.0, "SgstVal": 0.0, "IgstVal": 0.0, "CesVal": 0.0, "StCesVal": 0.0, "Discount": 0.0, "OthChrg": 0.0, "RndOffAmt": 0.0, "TotInvVal": 4525.00, "TotInvValFc": 0.0 }, "PayDtls": { "Nm": "ABCDE", "AccDet": "5697389713210", "Mode": "Cash", "FininsBr": "SBIN11000", "PayTerm": "100", "PayInstr": "Gift", "CrTrn": "test", "DirDr": "test", "CrDay": 100, "PaidAmt": 10000, "PaymtDue": 5000 } } } ]
源代码
string DATA = JsonConvert.SerializeObject(trans2, Newtonsoft.Json.Formatting.Indented); HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://einvoicing.internal.cleartax.co/v2/eInvoice/generate"); request.Method = "PUT"; request.Headers.Add("owner_id", "78c6beda-54a2-11ea-b064-0af3f8b02c24"); request.Headers.Add("gstin", "29AAFCD5862R000"); request.ContentType = "application/json"; request.ContentLength = DATA.Length; StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII); requestWriter.Write(DATA); requestWriter.Close(); try { WebResponse webResponse = request.GetResponse(); Stream webStream = webResponse.GetResponseStream(); StreamReader responseReader = new StreamReader(webStream); string response = responseReader.ReadToEnd(); Console.Out.WriteLine(response); responseReader.Close(); } catch() { }
这个错误通常意味着连接在建立或数据传输阶段被意外中断,既然Postman能正常调用,说明API本身没问题,问题大概率出在C#代码的细节处理上。我整理了几个关键修复点,按顺序尝试应该能解决问题:
1. 强制启用TLS 1.2或更高版本
现代API基本都不再支持TLS 1.0/1.1这类旧版本,Postman会自动使用最新的TLS协议,但HttpWebRequest在旧.NET框架中默认不会启用这些高版本协议。在创建请求前添加这行代码:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
如果你的.NET版本不支持Tls13,只保留Tls12即可。
2. 替换ASCII编码为UTF-8
你现在用ASCII编码写入请求流,但JSON里包含空格、特殊符号(比如地址里的逗号、空格),ASCII无法正确处理这些字符,会导致传输的数据损坏,API收到错误数据后就会中断连接。改成UTF-8编码:
// 替换原来的StreamWriter初始化代码 StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.UTF8);
3. 正确计算请求内容长度
直接用DATA.Length得到的是字符串的字符数,而不是实际传输的字节数(UTF-8中部分字符会占多个字节),这会导致ContentLength和实际发送的字节数不匹配,API可能会直接关闭连接。更稳妥的做法是先把JSON转成字节数组再计算长度:
byte[] dataBytes = Encoding.UTF8.GetBytes(DATA); request.ContentLength = dataBytes.Length; // 然后直接写入字节数组,不需要StreamWriter using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(dataBytes, 0, dataBytes.Length); }
4. 用using语句自动释放资源
你的代码里没有正确释放StreamWriter、WebResponse等资源,可能导致连接未正常关闭,引发异常。using语句会自动帮你处理资源释放,避免这类问题:
// 示例:用using包裹所有需要释放的资源 using (WebResponse webResponse = request.GetResponse()) using (Stream webStream = webResponse.GetResponseStream()) using (StreamReader responseReader = new StreamReader(webStream)) { string response = responseReader.ReadToEnd(); Console.Out.WriteLine(response); }
5. 完善错误捕获逻辑
当前的catch块是空的,你根本看不到具体的错误详情,这对排查问题非常不利。修改成这样,能帮你定位更具体的问题:
catch (Exception ex) { Console.WriteLine($"请求失败: {ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"内部错误: {ex.InnerException.Message}"); } }
优化后的完整代码
把上面的修复点整合后,代码应该是这样的:
string DATA = JsonConvert.SerializeObject(trans2, Newtonsoft.Json.Formatting.Indented); // 强制使用TLS 1.2+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://einvoicing.internal.cleartax.co/v2/eInvoice/generate"); request.Method = "PUT"; request.Headers.Add("owner_id", "78c6beda-54a2-11ea-b064-0af3f8b02c24"); request.Headers.Add("gstin", "29AAFCD5862R000"); request.ContentType = "application/json"; // 转换为字节数组并设置正确的内容长度 byte[] dataBytes = Encoding.UTF8.GetBytes(DATA); request.ContentLength = dataBytes.Length; // 写入请求体 using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(dataBytes, 0, dataBytes.Length); } try { // 用using自动释放资源 using (WebResponse webResponse = request.GetResponse()) using (Stream webStream = webResponse.GetResponseStream()) using (StreamReader responseReader = new StreamReader(webStream)) { string response = responseReader.ReadToEnd(); Console.Out.WriteLine(response); } } catch (Exception ex) { // 输出详细错误信息 Console.WriteLine($"请求失败: {ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"内部错误: {ex.InnerException.Message}"); } }
额外排查点
如果上面的修改还是没解决问题,可以试试这些:
- 给请求添加
User-Agent头:Postman会自动添加这个头,有些API会拒绝没有User-Agent的请求,添加一行:request.UserAgent = "Mozilla/5.0"; - 检查网络代理:如果你的环境需要代理,要配置
request.Proxy属性,确保代理能正确传递请求 - 对比Postman和代码的请求:用抓包工具对比两者的请求头、请求体是否完全一致,确保序列化后的JSON和你在Postman里用的完全相同
内容的提问来源于stack exchange,提问作者Materials

