如何以SoapMessage的形式发送数据并获得回复?

Har*_*lse 6 c# soap

我有一些数据需要以SOAP格式发送到服务器。该服务器将立即确认它已收到消息。几个小时后,我(可能从另一台服务器)收到了一条SOAP消息,其中包含有关已处理数据的信息。

我读了Stackoverflow:如何发送SOAP请求和接收响应。但是,答案是8岁。尽管它们可能仍然有效,但是可能存在更新的技术。

看起来确实如此:Microsoft具有System.Web.Services.Protocols,并且具有SoapMessage,SoapClientMessage,SoapServerMessage等类。

查看这些类,我会发现很多类似SOAP的类(标头,扩展名,客户端消息,服务器消息...通常,所提供的示例向我指示了这些类如何协同工作以及如何使用它们。只能找到有关如何处理已经存在的SOAP消息的示例。

给定一些需要发送的数据,如何将这些数据包装到这些SOAP类之一中并发送此消息?

这些类是否为此目的?还是我应该坚持2011年的方法,您可以通过上面提到的Stackoverflow问题所建议的方式自己将自己的XML数据格式化为SOAP格式来创建SOAP Web请求?

非常抱歉,通常我会写一些我尝试过的东西。las,我看不到提供的SoapMessage类之间的关系。我不知道如何使用它们。

评论后加法

我正在使用Windows Server / Visual Studio(最新版本)/。NET(最新版本)/ C#(最新版本)。

与服务器的通信是相互认证的。我需要用于与服务器通信的证书为PEM(CER / CRT)格式。私钥是RSA。该证书由适当的CA颁发,服务器还将使用适当的CA使用的证书。因此,我不需要创建新的证书(实际上,它不会被接受)。如果需要,我愿意使用OpenSsl之类的程序转换证书。

我尝试使用Apache TomCat进行通信,但是我觉得对于每天发送一条SOAP消息并每天等待一个答案的任务来说,这实在太多了。

也许因为Java对我来说是一种全新的技术,所以我很难看到接收到的消息的内容。回到C#和.NET。

我打算创建一个供控制台应用程序使用的DLL。该函数将流中的某些数据作为输入。它将创建肥皂消息,发送该消息,等待正确接收到该消息的答复,然后等待(可能几个小时)以获取一条包含处理后数据结果的新肥皂消息。为了使正确的报告和取消成为可能,我想最好使用async-await来做到这一点。

如果无法在一个应用程序中完成发送订单和等待结果的操作,我愿意创建一个Windows服务来监听输入,但我希望保持简单。

(虚拟)计算机将仅用于此任务,因此没有其他人需要侦听端口443。每天将发送一封定单消息,每天发送一条结果消息。

Sim*_*ier 3

以下是使用 HTTPS 的示例 C# 控制台客户端和服务器代码(它们位于同一示例中,但这仅用于演示目的)。

对于客户端,我们重用该类SoapHttpClientProtocol,但不幸的是,对于服务器端,我们无法重用任何内容,因为类完全与 ASP.NET (IIS)HttpContext类绑定

对于服务器端,我们使用,因此,根据您的配置,服务器端可能需要管理员HttpListener权限才能调用。HttpListenerPrefixes.Add(url)

该代码不使用客户端证书,但您可以将其添加到我放置 // TODO 注释的位置

该代码假定存在与所使用的 url 和端口关联的证书。如果没有(用于netsh http show sslcert转储所有关联的证书),您可以使用此处描述的过程添加一个:https ://stackoverflow.com/a/11457719/403671

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;

namespace SoapTests
{
    class Program
    {
        static void Main(string[] args)
        {
            // code presumes there is an sslcert associated with the url/port below
            var url = "https://127.0.0.1:443/";
            using (var server = new MyServer(url, MyClient.NamespaceUri))
            {
                server.Start(); // requests will occur on other threads
                using (var client = new MyClient())
                {
                    client.Url = url;
                    Console.WriteLine(client.SendTextAsync("hello world").Result);
                }
            }
        }
    }

    [WebServiceBinding(Namespace = NamespaceUri)]
    public class MyClient : SoapHttpClientProtocol
    {
        public const string NamespaceUri = "http://myclient.org/";

        public async Task<string> SendTextAsync(string text)
        {
            // TODO: add client certificates using this.ClientCertificates property
            var result = await InvokeAsync(nameof(SendText), new object[] { text }).ConfigureAwait(false);
            return result?[0]?.ToString();
        }

        // using this method is not recommended, as async is preferred
        // but we need it with this attribute to make underlying implementation happy
        [SoapDocumentMethod]
        public string SendText(string text) => SendTextAsync(text).Result;

        // this is the new Task-based async model (TAP) wrapping the old Async programming model (APM)
        public Task<object[]> InvokeAsync(string methodName, object[] input, object state = null)
        {
            if (methodName == null)
                throw new ArgumentNullException(nameof(methodName));

            return Task<object[]>.Factory.FromAsync(
                beginMethod: (i, c, o) => BeginInvoke(methodName, i, c, o),
                endMethod: EndInvoke,
                arg1: input,
                state: state);
        }
    }

    // server implementation
    public class MyServer : TinySoapServer
    {
        public MyServer(string url, string namespaceUri)
            : base(url)
        {
            if (namespaceUri == null)
                throw new ArgumentNullException(nameof(namespaceUri));

            NamespaceUri = namespaceUri;
        }

        // must be same as client namespace in attribute
        public override string NamespaceUri { get; }

        protected override bool HandleSoapMethod(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement)
        {
            switch (requestMethodElement.LocalName)
            {
                case "SendText":
                    // get the input
                    var text = requestMethodElement["text", NamespaceUri]?.InnerText;
                    text += " from server";

                    AddSoapResult(outputDocument, requestMethodElement, responseMethodElement, text);
                    return true;
            }
            return false;
        }
    }

    // simple generic SOAP server
    public abstract class TinySoapServer : IDisposable
    {
        private readonly HttpListener _listener;

        protected TinySoapServer(string url)
        {
            if (url == null)
                throw new ArgumentNullException(nameof(url));

            _listener = new HttpListener();
            _listener.Prefixes.Add(url); // this requires some rights if not used on localhost
        }

        public abstract string NamespaceUri { get; }
        protected abstract bool HandleSoapMethod(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement);

        public async void Start()
        {
            _listener.Start();
            do
            {
                var ctx = await _listener.GetContextAsync().ConfigureAwait(false);
                ProcessRequest(ctx);
            }
            while (true);
        }

        protected virtual void ProcessRequest(HttpListenerContext context)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));

            // TODO: add a call to context.Request.GetClientCertificate() to validate client cert
            using (var stream = context.Response.OutputStream)
            {
                ProcessSoapRequest(context, stream);
            }
        }

        protected virtual void AddSoapResult(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement, string innerText)
        {
            if (outputDocument == null)
                throw new ArgumentNullException(nameof(outputDocument));

            if (requestMethodElement == null)
                throw new ArgumentNullException(nameof(requestMethodElement));

            if (responseMethodElement == null)
                throw new ArgumentNullException(nameof(responseMethodElement));

            var result = outputDocument.CreateElement(requestMethodElement.LocalName + "Result", NamespaceUri);
            responseMethodElement.AppendChild(result);
            result.InnerText = innerText ?? string.Empty;
        }

        protected virtual void ProcessSoapRequest(HttpListenerContext context, Stream outputStream)
        {
            // parse input
            var input = new XmlDocument();
            input.Load(context.Request.InputStream);

            var ns = new XmlNamespaceManager(new NameTable());
            const string soapNsUri = "http://schemas.xmlsoap.org/soap/envelope/";
            ns.AddNamespace("soap", soapNsUri);
            ns.AddNamespace("x", NamespaceUri);

            // prepare output
            var output = new XmlDocument();
            output.LoadXml("<Envelope xmlns='" + soapNsUri + "'><Body/></Envelope>");
            var body = output.SelectSingleNode("//soap:Body", ns);

            // get the method name, select the first node in our custom namespace
            bool handled = false;
            if (input.SelectSingleNode("//x:*", ns) is XmlElement requestElement)
            {
                var responseElement = output.CreateElement(requestElement.LocalName + "Response", NamespaceUri);
                body.AppendChild(responseElement);

                if (HandleSoapMethod(output, requestElement, responseElement))
                {
                    context.Response.ContentType = "application/soap+xml; charset=utf-8";
                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    var writer = new XmlTextWriter(outputStream, Encoding.UTF8);
                    output.WriteTo(writer);
                    writer.Flush();
                    handled = true;
                }
            }

            if (!handled)
            {
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            }
        }

        public void Stop() => _listener.Stop();
        public virtual void Dispose() => _listener.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)