本地正常的C#&ASP.NET Core Web API在 staging服务器IIS中POST请求报错
.NET 6 API部署到Staging服务器IIS后请求外部WebService失败排查求助
我接触C#约3个月,项目包含.NET Standard 2.0的DLL和.NET 6.0的API,该API在本地Visual Studio运行、本地IIS发布运行均正常,但将发布文件复制到staging服务器IIS后无法正常工作。Staging服务器上其他.NET Framework项目运行正常,我的是唯一一个.NET Core项目。
环境信息
本地环境:
- Windows 10 Pro 22H2(版本19045.3930)
- Visual Studio 2022(版本17.8.5)
- IIS(版本10.0.19041.3691)
- 测试工具Insomnia(版本8.6.0)
Staging服务器环境:
- Windows Server 2016 Standard 1607(版本14393.4770)
- IIS(版本10.0.14393.0)
- .NET 6.0.2及多个.NET Core旧版本
错误信息
InnerException.StackTrace输出如下:
Error[[[An error occurred while sending the request.]]] at System.Net.HttpWebRequest.GetResponse() at Uni.Business.DFe.ConsumirBase.ExecutarServico(XmlDocument xml, Object servico, X509Certificate2 certificado) at Uni.Business.DFe.Servicos.ServicoBase.Executar() at Uni.Business.DFe.Servicos.NFCom.ServicoBase.Executar() at NFeModelo62.Handlers.RecepcaoHandler.HandleStatusServico62() in C:\repositorios\git\NFeModelo62\NFeModelo62Api\NFeModelo62Api\NFeModelo62\Handlers\RecepcaoHandler.cs:line 366 [[[An error occurred while sending the request.]]] at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.HttpMessageHandlerStage.Send(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpMessageHandlerStage.Send(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.SocketsHttpHandler.Send(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpMessageInvoker.Send(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.Send(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) at System.Net.HttpWebRequest.SendRequest(Boolean async) at System.Net.HttpWebRequest.GetResponse()
相关代码与参数
触发异常的ExecutarServico方法代码如下:
/// <summary> /// Estabelece conexão com o Webservice e faz o envio do XML e recupera o retorno. Conteúdo retornado pelo webservice pode ser recuperado através das propriedades RetornoServicoXML ou RetornoServicoString. /// </summary> /// <param name="xml">XML a ser enviado para o webservice</param> /// <param name="servico">Parâmetros para execução do serviço (parâmetros do soap)</param> /// <param name="certificado">Certificado digital a ser utilizado na conexão com os serviços</param> public void ExecutarServico(XmlDocument xml, object servico, X509Certificate2 certificado) { var soap = (WSSoap)servico; if (certificado == null && soap.UsaCertificadoDigital) { throw new CertificadoDigitalException(); } TratarScapeEnvio = false; TratarScapeRetorno = false; if (soap.SoapString.IndexOf("{xmlBodyScape}") > 0) { TratarScapeEnvio = true; TratarScapeRetorno = true; } else if (soap.SoapString.IndexOf("{xmlBodyScapeEnvio}") > 0) { TratarScapeEnvio = true; TratarScapeRetorno = false; } else if (soap.SoapString.IndexOf("{xmlBodyScapeRetorno}") > 0) { TratarScapeEnvio = false; TratarScapeRetorno = true; } var urlpost = new Uri(soap.EnderecoWeb); var soapXML = EnveloparXML(soap, xml.OuterXml); var buffer2 = Encoding.UTF8.GetBytes(soapXML); ServicePointManager.Expect100Continue = false; ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(RetornoValidacao); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(urlpost); httpWebRequest.Headers.Add("SOAPAction: " + soap.ActionWeb); httpWebRequest.CookieContainer = cookies; httpWebRequest.Timeout = soap.TimeOutWebServiceConnect; httpWebRequest.ContentType = (string.IsNullOrEmpty(soap.ContentType) ? "application/soap+xml; charset=utf-8;" : soap.ContentType); httpWebRequest.Method = "POST"; if (soap.UsaCertificadoDigital) { httpWebRequest.ClientCertificates.Add(certificado); } httpWebRequest.ContentLength = buffer2.Length; //Definir dados para conexão com proxy if (soap.Proxy != null) { httpWebRequest.Proxy = soap.Proxy; } var postData = httpWebRequest.GetRequestStream(); postData.Write(buffer2, 0, buffer2.Length); postData.Close(); WebException webException = null; WebResponse responsePost = null; try { responsePost = (HttpWebResponse)httpWebRequest.GetResponse(); } catch (Exception e) { throw; } //catch (WebException ex) //{ // webException = ex; // responsePost = ex.Response; // if (ex.Response == null) // { // throw (ex); // } //} var streamPost = responsePost.GetResponseStream(); var encoding = Encoding.GetEncoding(soap.EncodingRetorno); var streamReaderResponse = new StreamReader(streamPost, encoding); var conteudoRetorno = streamReaderResponse.ReadToEnd(); var retornoXml = new XmlDocument(); try { retornoXml.LoadXml(conteudoRetorno); } catch (XmlException ex) { if (webException != null) { throw (webException); } throw (ex); } catch (Exception ex) { throw (ex); } if (soap.TagRetorno.ToLower() != "prop:innertext") { var tagRetorno = soap.TagRetorno; if (soap.TipoAmbiente == TipoAmbiente.Homologacao && !string.IsNullOrWhiteSpace(soap.TagRetornoHomologacao)) { tagRetorno = soap.TagRetornoHomologacao; } if (retornoXml.GetElementsByTagName(tagRetorno)[0] == null) { throw new Exception("Não foi possível localizar a tag <" + tagRetorno + "> no XML retornado pelo webservice.\r\n\r\n" + "Conteúdo retornado pelo servidor:\r\n\r\n" + retornoXml.InnerXml); } if (TratarScapeRetorno) { RetornoServicoString = retornoXml.GetElementsByTagName(tagRetorno)[0].ChildNodes[0].InnerText; } else { RetornoServicoString = retornoXml.GetElementsByTagName(tagRetorno)[0].ChildNodes[0].OuterXml; } } else { if (string.IsNullOrWhiteSpace(retornoXml.InnerText)) { throw new Exception("A propriedade InnerText do XML retornado pelo webservice está vazia."); } RetornoServicoString = retornoXml.InnerText; //Remover do XML retornado o conteúdo <?xml version="1.0" encoding="utf-8"?> ou gera falha na hora de transformar em XmlDocument if (RetornoServicoString.IndexOf("?>") >= 0) { RetornoServicoString = RetornoServicoString.Substring(RetornoServicoString.IndexOf("?>") + 2); } //Remover quebras de linhas RetornoServicoString = RetornoServicoString.Replace("\r\n", ""); } if (soap.PadraoNFSe == PadraoNFSe.FIORILLI || soap.PadraoNFSe == PadraoNFSe.SONNER || soap.PadraoNFSe == PadraoNFSe.SMARAPD || soap.PadraoNFSe == PadraoNFSe.DSF) { RetornoServicoString = RetornoServicoString.Replace("ns1:", string.Empty); RetornoServicoString = RetornoServicoString.Replace("ns2:", string.Empty); RetornoServicoString = RetornoServicoString.Replace("ns3:", string.Empty); RetornoServicoString = RetornoServicoString.Replace("ns4:", string.Empty); } else if (soap.PadraoNFSe == PadraoNFSe.SIMPLE) { RetornoServicoString = RetornoServicoString.Replace("m:", string.Empty); } RetornoServicoXML = new XmlDocument { PreserveWhitespace = false }; RetornoServicoXML.LoadXml(RetornoServicoString); }
传入的XmlDocument.OuterXml参数:
<?xml version="1.0" encoding="utf-8"?> <consStatServNFCom versao="1.00" xmlns="http://www.portalfiscal.inf.br/nfcom"> <tpAmb>2</tpAmb> <xServ>STATUS</xServ> </consStatServNFCom>
Servico对象(WebString Soap)信息:
Web URL = http://www.portalfiscal.inf.br/nfcom/wsdl/NFComStatusServico Content Type = application/soap+xml; charset=utf-8; Return Tag = nfcomResultMsg Soap String = <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><nfcomDadosMsg xmlns="http://www.portalfiscal.inf.br/nfcom/wsdl/NFComStatusServico">{xmlBody}</nfcomDadosMsg></soap:Body></soap:Envelope>
已尝试的排查步骤
- 注释原异常捕获代码以排查问题,但无进展;
- 在本地安装IIS模拟服务器环境,未复现错误;
- 查阅IIS、HttpWebRequest相关微软文档,未找到环境与请求问题的关联;
- 开启stdoutLogEnable但未生成相关日志。
web.config内容
<?xml version="1.0" encoding="utf-8"?> <configuration> <location path="." inheritInChildApplications="false"> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\NFeModelo62Api.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" /> </system.webServer> </location> </configuration> <!--ProjectGuid: 4c7ebfbf-36d8-47ca-bc46-a62f36c09fa6-->
排查建议
- 检查服务器网络连通性:在Staging服务器上直接访问目标WebService地址,确认网络能正常访问该地址,排查防火墙、代理或网络策略是否限制了.NET Core进程的出站请求。
- 升级.NET 6版本:当前服务器安装的是.NET 6.0.2,该版本存在一些已知的网络相关bug,建议升级到.NET 6的最新稳定版本,再重新部署测试。
- 配置代理设置:如果服务器需要通过代理访问外部网络,检查代码中
soap.Proxy是否正确配置,或者在服务器全局配置.NET Core的代理(通过环境变量HTTP_PROXY/HTTPS_PROXY)。 - 捕获详细异常信息:修改代码中的异常捕获逻辑,捕获
WebException并输出Response.StatusCode、Response.StatusDescription以及响应内容,获取更具体的错误原因。示例修改:
try { responsePost = (HttpWebResponse)httpWebRequest.GetResponse(); } catch (WebException ex) { if (ex.Response is HttpWebResponse errorResponse) { var statusCode = errorResponse.StatusCode; var statusDesc = errorResponse.StatusDescription; using var reader = new StreamReader(errorResponse.GetResponseStream()); var errorContent = reader.ReadToEnd(); throw new Exception($"请求失败: {statusCode} - {statusDesc}\n响应内容: {errorContent}", ex); } throw; }
- 检查IIS应用程序池权限:确保.NET Core应用的IIS应用程序池身份有足够的权限发起网络请求,以及访问证书(如果使用了证书)。可以临时改为
LocalSystem身份测试是否是权限问题。 - 启用详细日志:确认
stdoutLogFile路径的目录存在且应用程序池身份有写入权限,或者修改为绝对路径;同时检查Windows事件查看器中的应用程序日志,查看是否有.NET Core相关的错误信息。
内容的提问来源于stack exchange,提问作者Rod Limao
相关产品推荐
相关产品推荐

