我的应用程序有自己的脚本语言,我无法摆脱它(编写了大量客户特定的脚本)。现在我的客户询问是否可以从该脚本语言中调用 SOAP 服务。当然,每个客户需要调用的 SOAP 服务会有所不同。这给我留下了几个选择:
- 使用 WSDL 实用程序生成特定于客户的 SOAP 客户端代理并将特定于客户的逻辑放入我的应用程序中
- 使用 WSDL 实用程序生成特定于客户的 SOAP 客户端代理,将特定于客户的逻辑放入特定于客户的 DLL 中,并预见一个插件系统,应用程序可以在其中以通用方式调用插件
- 编写一个动态生成 SOAP 调用的通用模块
在我的情况下,前两个选项不是真正的选择,因为我不希望应用程序中有任何客户特定的逻辑或客户特定的 DLL。
对我来说,从长远来看,第 3 个选项是最好的,因为它允许我的顾问同事通过我的脚本语言调用 SOAP 服务,而无需进行任何客户特定的开发。向我的脚本语言动态添加函数不是问题,生成动态 SOAP 调用是问题。
我首先查看了 WSDL 实用程序的输出。然后我开始删除东西,直到它不再起作用。以下代码仍然有效:
[System::CodeDom::Compiler::GeneratedCodeAttribute(L"wsdl", L"4.0.30319.1"),
System::Diagnostics::DebuggerStepThroughAttribute,
System::ComponentModel::DesignerCategoryAttribute(L"code"),
System::Web::Services::WebServiceBindingAttribute(Name=L"MyOwnScriptingSoapClient", Namespace=L"http://microsoft.com/webservices/")]
public ref class MyWebService : public System::Web::Services::Protocols::SoapHttpClientProtocol
{
public:
MyWebService() {}
public:
[System::Web::Services::Protocols::SoapDocumentMethodAttribute(L"http://microsoft.com/webservices/GetPrimeNumbers", RequestNamespace=L"http://microsoft.com/webservices/",
ResponseNamespace=L"http://microsoft.com/webservices/", Use=System::Web::Services::Description::SoapBindingUse::Literal, ParameterStyle=System::Web::Services::Protocols::SoapParameterStyle::Wrapped)]
System::String^ GetPrimeNumbers(System::Int32 max);
};
inline System::String^ MyWebService::GetPrimeNumbers(System::Int32 max) {
cli::array< System::Object^ >^ results = this->Invoke(L"GetPrimeNumbers", gcnew cli::array< System::Object^ >(1) {max});
return (cli::safe_cast<System::String^ >(results[0]));
}
通过设置 Url 属性可以动态地设置 Web 服务的 URL,但是我找不到使方法名称动态的方法。
添加这样的通用方法似乎仍然有效:
...
[System::Web::Services::Protocols::SoapDocumentMethodAttribute(L"http://microsoft.com/webservices/GetPrimeNumbers", RequestNamespace=L"http://microsoft.com/webservices/",
ResponseNamespace=L"http://microsoft.com/webservices/", Use=System::Web::Services::Description::SoapBindingUse::Literal, ParameterStyle=System::Web::Services::Protocols::SoapParameterStyle::Wrapped)]
cli::array< System::Object^ >^ CallWs(cli::array< System::Object^ >^ args);
...
inline cli::array< System::Object^ >^ MyWebService::CallWs(cli::array< System::Object^ >^ args) {
cli::array< System::Object^ >^ results = this->Invoke(L"GetPrimeNumbers", args);
return results;
但是一旦我删除 GetPrimeNumbers 方法,调用就不再起作用并报告以下错误:
Unhandled Exception: System.ArgumentException: GetPrimeNumbers Web Service method name is not valid.
at System.Web.Services.Protocols.SoapHttpClientProtocol.BeforeSerialize(WebRequest request, String methodName, Object[] parameters)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at MyWebService.CallWs(Object[] args)
at main(Int32 argc, SByte** argv)
at _mainCRTStartup()
此外,更改 SoapDocumentMethodAttribute 属性中的 Web 服务名称(例如,更改为 GetPrimo)也会产生同样的错误。
因此,我的问题:
- 继续走这条路是否有意义,即查看 WSDL 生成的逻辑试图“概括”对(任何)SOAP 服务的调用,或者这根本不会起作用?
- 还有其他以动态方式(使用.Net)生成 SOAP 调用的好方法吗?
- 或者是自己创建 XML(Soap Envelope)以进行 SOAP 调用的唯一方法?
- 有没有机会找到一些我可以继续工作的示例代码?
在此先感谢,帕特里克