【问题标题】:SOAPAction over HTTP in .NET Core vs .NET Framework.NET Core 与 .NET Framework 中基于 HTTP 的 SOAPAction
【发布时间】:2020-06-21 08:49:10
【问题描述】:

我在较旧的应用程序中有以下代码:

        //  send request
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.ContentType = "text/xml;charset=UTF-8";
        req.Method = "POST";
        req.Accept = "text/xml";
        req.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
        req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

        var test = req.Headers.ToString();

        if (string.IsNullOrWhiteSpace(SOAPaction))
            req.Headers.Add("SOAPAction", "\"\"");
        else
            req.Headers.Add("SOAPAction", "\"" + SOAPaction + "\"");

        using (var writer = new StreamWriter(req.GetRequestStream(), Encoding.UTF8))
        {
            xdoc.Save(writer, SaveOptions.DisableFormatting);
        }

        WebResponse response;
        try
        {
            response = req.GetResponse();

            System.Diagnostics.Debug.WriteLine(response.ToString());
        }
        catch (WebException we)
        {
            if (we.Response.ContentType.StartsWith("text/xml", StringComparison.InvariantCultureIgnoreCase))
            {
                using (var stream = we.Response.GetResponseStream())
                {
                    var xml = XDocument.Load(stream);
                    var fault = xml.Descendants().FirstOrDefault(e => e.Name.LocalName == "faultstring").Value;
                    throw new Exception("Error received when invoking the method: " + fault);
                }
            }
            else
                throw we;
        }

我正在尝试将其移植到新的 .NET Core 应用程序,但在更改必要的标头后,我总是收到错误消息:“远程服务器返回错误:(500) 内部服务器错误”,这并不完全有帮助。我知道 HttpWebRequest 在 .NET Framework 和 .NET Core 中的工作方式不同,但是在 .NET Core 中设置标头后,它应该只进行相同的调用吗?

【问题讨论】:

  • 查看服务器日志以获取有关 500 的更多详细信息。如果不可用,请检查客户端的内部异常和更多异常详细信息。 ssl? tls 客户端版本?
  • 我无权访问服务器日志。 InnerException 为 null,并且客户端 SecurityProtocol 设置为 SecurityProtocolType.Tls12。这是在 .NET Framework 应用程序上手动完成的,该应用程序可以正常工作并且在 .NET Core 中应该是默认设置,但是当我手动设置时它不会改变任何内容
  • 尝试添加:req.ProtocolVersion = HttpVersion.Version10;如果是旧代码,它可能使用的是 http 1.0 而不是默认的 http 1.1。
  • 这不起作用。我还在 .NET Framework 中的旧/工作版本中尝试过这个,它对 Version10 和 Version11 都给出了正确的响应

标签: c# .net soap .net-core httpwebrequest


【解决方案1】:

我不确定服务器为什么拒绝它,因为您的请求代码对我来说似乎很好。 (500) Internal Server Error 是服务器端错误。大多数情况下,它来自请求正文或标头或 url 本身,服务器拒绝了它。所以需要从url开始调试。我发现有时必须在 url 的末尾指定?WSDL,如果没有指定,它将返回500 internal server error。所以,先检查一下。如果问题仍然存在,请检查正文并在 SoapUIPostman 等测试工具上对其进行测试,确保它使用的内容和标头与您在请求中使用的相同。

另外,我建议改为移动到HttpClient(因为如果可能的话,它已经移动到了。因为HttpWebRequest 是一个死区。HttpClient 是建立在它之上的,这样会更好并且比传统的HttpWebRequest 更易于维护(而且它是用开箱即用的async 构建的)。

如果您坚持使用HttpWebRequest,那么您可以实现自己的类,这将使事情更容易维护。我已经为我以前的项目专门为 Soap API 及其信封构建了一个小型且基本的类。因为我不想在每个类似的项目上重写相同的东西。我希望它会有用:

Soap API 类

public class SoapAPI : IDisposable
{
    // Requirements for Soap API 
    private const string SoapAction = "SOAPAction";
    private const string SoapContentType = "text/xml;charset=\"utf-8\"";
    private const string SoapAccept = "text/xml";
    private const string SoapMethod = "POST";

    private HttpWebRequest _request;

    private SoapEnvelope _soapEnvelope;

    public class SoapAPIResponse
    {
        private readonly SoapAPI _instance;

        public SoapAPIResponse(SoapAPI instance)
        {
            _instance = instance;
        }
        public HttpWebResponse AsWebResponse()
        {
            try
            {
                _instance.SendRequest(_instance._soapEnvelope.GetEnvelope());

                return (HttpWebResponse)_instance._request.GetResponse();
            }
            catch (WebException ex)
            {
                throw ex;
            }

        }

        public StreamReader AsStreamReader()
        {
            return new StreamReader(AsHttpWebResponse().GetResponseStream());
        }

        public string AsString()
        {
            return AsStreamReader().ReadToEnd();
        }
    }

    public SoapAPI(string url, SoapEnvelope envelope)
    {
        if (string.IsNullOrEmpty(url)) { throw new ArgumentNullException(nameof(url)); }

        if (envelope == null) { throw new ArgumentNullException(nameof(envelope)); }

        // some Soap Services requires to target the xml schema ?wsdl at the end of the url, just uncomment if needed. 
        // url = url.LastIndexOf("?WSDL", StringComparison.InvariantCultureIgnoreCase) > 0 ? url : $"{url}?WSDL";

        _request = (HttpWebRequest)WebRequest.Create(new Uri(url));

        _request.Headers.Clear();

        _request.Headers.Add(SoapAction, envelope.SoapActionName);

        _request.ContentType = SoapContentType;

        _request.Accept = SoapAccept;

        _request.Method = SoapMethod;

        // optimizations 
        _request.ServicePoint.ConnectionLimit = 8;
        _request.Proxy = null;
        _request.KeepAlive = true;
        _request.ReadWriteTimeout = 300000;

        // envelope
        _soapEnvelope = envelope;
    }

    public SoapAPI AddHeader(string name, string value)
    {
        _request.Headers.Add(name, value);
        return this;
    }

    public SoapAPI AddHeader(HttpRequestHeader header, string value)
    {
        _request.Headers.Add(header, value);
        return this;
    }

    public SoapAPI AddCompressionMethod(DecompressionMethods methods)
    {
        _request.AutomaticDecompression = methods;
        return this;
    }

    private void SendRequest(object obj)
    {
        using StreamWriter sw = new StreamWriter(_request.GetRequestStream(), Encoding.UTF8);
        sw.Write(obj);
    }

    public SoapAPIResponse GetResponse()
    {
        return new SoapAPIResponse(this);
    }



    #region IDisposable

    private bool _disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _request = null;
                _soapEnvelope = null;
            }

            _disposed = true;
        }
    }


    ~SoapAPI()
    {
        Dispose(false);
    }

    #endregion

}

SoapEnvelope 类

public class SoapEnvelope : IDisposable
{
    private XDocument _envelope;

    public XNamespace SoapNamespace = "http://schemas.xmlsoap.org/soap/envelope/";

    public XNamespace SoapActionNamespace { get; }

    public string SoapActionName { get; }

    public SoapEnvelope(XNamespace actionNamespace, string actionName)
    {
        if (actionNamespace == null) { throw new ArgumentNullException(nameof(actionNamespace)); }

        if (string.IsNullOrEmpty(actionName)) { throw new ArgumentNullException(nameof(actionName)); }

        SoapActionNamespace = actionNamespace;
        SoapActionName = actionName;
        CreateEnvelope();
    }

    private void CreateEnvelope()
    {
        _envelope = new XDocument(
            new XElement(SoapNamespace + "Envelope",
            new XAttribute(XNamespace.Xmlns + "soapenv", SoapNamespace),
            new XAttribute(XNamespace.Xmlns + SoapActionName, SoapActionNamespace),
            new XElement(SoapNamespace + "Header"),
            new XElement(SoapNamespace + "Body")
            )
         );
    }

    public SoapEnvelope AddHeaderElement(XElement elements)
    {
        _envelope.Root.Element(SoapNamespace + "Header").Add(elements);
        return this;
    }

    public SoapEnvelope AddBodyElement(XElement elements)
    {
        _envelope.Root.Element(SoapNamespace + "Body").Add(elements);
        return this;
    }

    public XDocument GetEnvelope()
    {
        return _envelope;
    }

    public bool IsValidXml(string xml)
    {
        try
        {
            XmlConvert.VerifyXmlChars(xml);
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

    #region IDisposable

    private bool _disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _envelope = null;
                SoapNamespace = null;
            }

            _disposed = true;
        }
    }


    ~SoapEnvelope()
    {
        Dispose(false);
    }

    #endregion
}

这里是基本用法:

WebResponse response; // will hold the response. 

XNamespace actionNamespace = "http://xxxx"; // the namespace 
var SoapAction = ""; //the action value  

var envelope = 
    new SoapEnvelope(actionNamespace, SoapAction)
        .AddBodyElement(new XElement(...)); 

using (var request = new SoapAPI(url, envelope))
{
    response = request
    .AddCompressionMethod(DecompressionMethods.GZip | DecompressionMethods.Deflate)
    .AddHeader(HttpRequestHeader.AcceptEncoding, "gzip,deflate")
    .GetResponse()
    .AsWebResponse(); // you can get the respnse as StreamReader, WebResponse, and String 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 2018-07-10
    • 2020-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    相关资源
    最近更新 更多