如何从对象类型创建 SOAP 请求?

How can I create a SOAP request from an object type?

我先说说我的意图,然后进入我的问题。

我正在尝试构建一个与 SOAP 服务通信的系统,其中 SOAP 请求在 运行 时有些未知。最后我需要做的是从未知对象生成一个 SOAP 请求。我将从头开始动态创建具有适当属性和特性的对象,然后将其传递到我的服务“请求”方法以发送到 SOAP 服务。这是我一直在使用的代码,然后是我收到的错误。

SOAP 客户端:

/// <summary>
/// GOSService proxy class
/// </summary>
[DebuggerStepThroughAttribute()]
[DesignerCategoryAttribute("code")]
[WebServiceBindingAttribute(Name = "ServiceSoapBinding", Namespace = "service.domain.com", ConformsTo = WsiProfiles.None)]
[SoapRpcService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public partial class TestService : SoapHttpClientProtocol
{
    /// <summary>
    /// Initializes a new instance of the TestService class.
    /// </summary>
    public TestService()
    {
        this.Url = "https://restsv01.domain.com/ServiceTest/services/TestService";
        ServicePointManager.Expect100Continue = true;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
        ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnRemoteCertificateValidationCallback);
    }

    /// <summary>
    /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication.
    /// </summary>
    /// <param name="sender">An object that contains state information for this validation.</param>
    /// <param name="certificate">The certificate used to authenticate the remote party.</param>
    /// <param name="chain">The chain of certificate authorities associated with the remote certificate.</param>
    /// <param name="sslPolicyErrors">One or more errors associated with the remote certificate.</param>
    /// <returns>A Boolean value that determines whether the specified certificate is accepted for authentication.</returns>
    private bool OnRemoteCertificateValidationCallback(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        return true;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="order"></param>
    /// <returns></returns>
    [SoapRpcMethodAttribute("", RequestNamespace = "service.domain.com", ResponseNamespace = "service.domain.com")]
    [SampleSoap.LoggerSoapExtensionAttribute]
    [return: SoapElementAttribute("requestReturn")]
    public object request(object parm)
    {
        object[] results = this.Invoke("request", new object[] { parm });
        return ((object)(results[0]));
    }}

测试模型: (不会有任何预定义的模型,它们将是动态生成的。但出于测试目的,我使用此模型进行测试)

    [SerializableAttribute()]
    [DebuggerStepThroughAttribute()]
    [DesignerCategoryAttribute("code")]
    [SoapTypeAttribute(Namespace = "http://entity.domain.com")]
    public class ParentNode
    {
        private string nameField = "1";
        [SoapElementAttribute(IsNullable = true)]
        public string Name
        {
            get { return this.nameField; }
            set { this.nameField = value; }
        }
    }

测试调用代码:

        Services.Soap.Models.ParentNode parent = new Services.Soap.Models.ParentNode();
        parent.Name = "John Doe";
        Services.Soap.TestService service = new Services.Soap.TestService();
        object resp = service.request(parent);

当我运行这段代码时,错误发生在这一行:

object[] results = this.Invoke("request", new object[] { parm });

这是错误:

The type Services.Soap.Models+ParentNode was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

现在,如果我将服务“请求”方法的参数更改为强类型,请求构建良好并传递给 SOAP 服务,就像这样。

public object request(ParentNode parm)

我已经尝试了大约 50 种方法来让它工作,包括将 Type 作为参数传递给请求方法,并创建要传递的对象的“动态”实例。

    public object request(object parm, Type t)
    {
        dynamic converted = Convert.ChangeType(parm, t);

        object[] results = this.Invoke("request", new object[] { converted });
        return ((object)(results[0]));
    } 

这不起作用,因为“已转换”仍被视为对象类型。

我也试过在“GetWriterForMessage”方法中拦截 soap 信封,这样我就可以构建自己的信封,但我无法利用它。

所以我的问题是,我怎样才能让参数类型为对象的SOAP请求成功构建呢?我是否应该采取另一种方法来使我的架构正常工作?

我对您正在实施的系统的描述不是很清楚。你提到你会发送 "dynamic" 请求到 SOAP 服务器?或者您正在使用 "dynamic" 操作创建一些服务。这些细节对我来说有点模糊,但我记下了一些想法。

简而言之,如果您想在 SOAP 中这样做,您就是在为自己打开一个痛苦的世界。 REST 可能更合适,因为它没有 WSDL,但如果您使用 WADL、RAML 和这些新兴标准,您的情况可能会有所不同。我不会尝试甚至为您回答技术问题,而是试图让您明白为什么这是通往维护地狱的单程票。

此实现确实打破了 SOA 概念(尤其是标准化服务契约),这可能不适用于您的情况,因为仅实现具有未知内容的动态 WSDL 会使体系结构不符合非 SOA 的资格。这也与拥有 WSDL 的全部意义相矛盾。 WSDL 是 XML 中的接口定义。因此,动态服务需要一个动态接口,然后将其限定为非接口。因此,当您使用对象类型作为参数时,WSDL 和接口不匹配。

当其他系统使用您的 SOAP 服务时,它们将基于已发布的 WSDL 进行操作。如果 WSDL 发生变化,他们的程序将中断,因为定义或 WSDL 与他们生成的数据类型不匹配。在 HTTP 上运行并获取请求并发送回复并且没有 WSDL 的服务不是 SOAP 服务,它是使用 HTTP 端口的基于 XML 的服务。它们根本不同。您的服务提供商(即您从中请求数据的 SOAP 服务)是否没有 WSDL?如果不是,这些不是您要找的机器人:)

接口是应用程序可以用来查询服务的约定契约。本质上,您是在谈论结构化数据对象与非结构化数据对象(至少在 运行 时间)。

如果您想要这种类型的模式,您需要查看发布订阅架构,例如使用轻量级的 MQTT,或者另一个示例是带有主题的 JMS。

如果您正在使用您提到的 Web 服务,将会有一个 WSDL,您的 .Net 应用程序可以使用它来生成数据类型和关联的 C# 类。 类 将包括正在传输的数据和服务上公开的操作。

在官方 SOAP 世界中,没有动态服务这样的东西,因为它打破了我们同意作为服务器和客户端在操作期间通过这种形式 XML 进行通信的整个范例。