Как отправить данные в виде SoapMessage и получить ответ? - PullRequest
6 голосов
/ 08 апреля 2019

У меня есть некоторые данные, которые нужно отправить на сервер в формате SOAP. Этот сервер немедленно подтвердит, что получил сообщения. Через несколько часов я получаю (возможно, с другого сервера) сообщение SOAP, содержащее информацию об обработанных данных.

Я прочитал Stackoverflow: Как отправить запрос SOAP и получить ответ . Тем не менее, ответы 8 лет. Хотя они все еще могут работать, возможно, существуют более новые методы.

И действительно, кажется: у Microsoft есть System.Web.Services.Protocols, с такими классами, как SoapMessage , SoapClientMessage, SoapServerMessage и т. Д.

Глядя на классы, я нахожу множество SOAP-подобных классов (заголовки, расширения, клиентские сообщения, серверные сообщения ... Обычно приведенные примеры дают мне представление о том, как эти классы работают вместе и как их использовать. Документы MSDN Я могу только найти примеры того, как обрабатывать уже существующие сообщения SOAP.

Учитывая некоторые данные, которые нужно отправить, как я могу как-то обернуть эти данные в один из этих классов SOAP и отправить это сообщение?

Эти классы предназначены для этой цели? Или я должен придерживаться метода 2011 года, в котором вы создадите веб-запрос SOAP, отформатировав данные XML в формате мыла, как подсказывает вышеупомянутый вопрос Stackoverflow?

Мне очень жаль, обычно я бы писал вещи, которые я пробовал. Увы, я не вижу связи между предоставляемыми классами SoapMessage . Понятия не имею, как их использовать.

Добавление после комментариев

Я использую Windows Server / Visual Studio (новейшие версии) / .NET (новейшие версии) / C # (новейшие версии).

Связь с сервером является взаимной аутентификацией. Сертификат, который мне нужно использовать для связи с сервером, имеет формат PEM (CER / CRT). Закрытый ключ - RSA. Этот сертификат выдается надлежащим центром сертификации, сервер также будет использовать сертификаты, используемые соответствующим центром сертификации. Поэтому мне не нужно создавать новый сертификат (на самом деле, он не будет принят). При необходимости я готов конвертировать сертификаты, используя такие программы, как OpenSsl и т. П.

Я пытался использовать Apache TomCat для общения, но мне кажется, что это слишком много для задачи отправки одного сообщения SOAP в день и ожидания одного ответа в день.

Может быть, из-за того, что java - это совершенно новый метод для меня, мне было трудно увидеть содержимое полученных сообщений. Итак, вернемся к C # и .NET.

Я планировал создать DLL, которая будет использоваться консольным приложением. Функция будет иметь некоторые данные в потоке в качестве входных данных. Он будет создавать мыльное сообщение, отправлять его, ждать ответа о том, что сообщение было получено правильно, и ждать (возможно, несколько часов) нового сообщения Soap, содержащего результаты обработанных данных. Чтобы сделать правильные отчеты и отмена возможными, я думаю, что лучше всего сделать это с помощью async-await

Если отправка заказа и ожидание результата не могут быть выполнены в одном приложении, я хочу создать службу Windows, которая будет прослушивать ввод, но я предпочитаю сделать ее простой.

(Виртуальный) компьютер будет использоваться только для этой задачи, поэтому больше никому не нужно будет слушать порт 443. Будет отправляться одно сообщение с заказом в день и одно сообщение с результатом в день.

Ответы [ 3 ]

3 голосов
/ 17 апреля 2019

Вот пример клиентского и серверного кода C # Console (они в одном и том же примере, но это, конечно, только для демонстрационных целей), который использует HTTPS.

Для клиентской стороны мы повторно используем SoapHttpClientProtocol class, но, к сожалению, для серверной части мы ничего не можем использовать повторно, потому что классы полностью привязаны к ASP.NET (IIS) HttpContext class

. Для серверной части мы используем HttpListener, поэтомув зависимости от вашей конфигурации, на стороне сервера, вероятно, потребуются права администратора, чтобы иметь возможность вызывать HttpListener Prefixes.Add(url).

Код не использует сертификат клиента, но вы можете добавить его там, где я разместил// TODO comments

Код предполагает наличие сертификата, связанного с используемым 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();
    }
}
3 голосов
/ 12 апреля 2019

Лично я использую ServiceStack для создания как клиента, так и сервера.

https://docs.servicestack.net/soap-support

Или SoapHttpClient nuget

https://github.com/pmorelli92/SoapHttpClient

Или мой примериз далекого прошлого, когда

Возможно ли, что я смогу преобразовать простую строку в сообщение SOAP и отправить ее?

Ответ зависит от того, какую платформу или библиотеки вы планируетеиспользовать?

0 голосов
/ 18 апреля 2019

Самый простой современный ответ - объявить простой класс, который определяет структуру вашего сообщения, а затем сериализовать его, используя HttpClient для его отправки.

Однако SOAP - это стандарт, созданный для обмена сообщениями на основе описания, поэтомуСоответствующая рекомендация состоит в том, чтобы сгенерировать ваш клиентский код из описания wsdl, используя «ссылку на службу», а затем использовать сгенерированный клиентский объект.

Однако я бы порекомендовал, как и другие, указали, что вместо этого вы пытаетесь перейти к службам REST.(при условии, что это возможно).Код менее сложен, система намного проще в использовании и является глобальным стандартом.

Вот сравнение и пример обоих ...

https://smartbear.com/blog/test-and-monitor/understanding-soap-and-rest-basics/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...