You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地正常的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-->

排查建议

  1. 检查服务器网络连通性:在Staging服务器上直接访问目标WebService地址,确认网络能正常访问该地址,排查防火墙、代理或网络策略是否限制了.NET Core进程的出站请求。
  2. 升级.NET 6版本:当前服务器安装的是.NET 6.0.2,该版本存在一些已知的网络相关bug,建议升级到.NET 6的最新稳定版本,再重新部署测试。
  3. 配置代理设置:如果服务器需要通过代理访问外部网络,检查代码中soap.Proxy是否正确配置,或者在服务器全局配置.NET Core的代理(通过环境变量HTTP_PROXY/HTTPS_PROXY)。
  4. 捕获详细异常信息:修改代码中的异常捕获逻辑,捕获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;
}
  1. 检查IIS应用程序池权限:确保.NET Core应用的IIS应用程序池身份有足够的权限发起网络请求,以及访问证书(如果使用了证书)。可以临时改为LocalSystem身份测试是否是权限问题。
  2. 启用详细日志:确认stdoutLogFile路径的目录存在且应用程序池身份有写入权限,或者修改为绝对路径;同时检查Windows事件查看器中的应用程序日志,查看是否有.NET Core相关的错误信息。

内容的提问来源于stack exchange,提问作者Rod Limao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:49:54