我有一些数据需要以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。每天将发送一封定单消息,每天发送一条结果消息。
以下是使用 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)
| 归档时间: |
|
| 查看次数: |
295 次 |
| 最近记录: |