从自己的脚本语言动态调用 SOAP 服务

发布于 2024-09-14 00:38:48 字数 3435 浏览 2 评论 0原文

我的应用程序有自己的脚本语言,我无法摆脱它(编写了许多特定于客户的脚本)。现在,我的客户询问是否可以从该脚本语言中调用 SOAP 服务。当然,每个客户需要调用的SOAP服务都会有所不同。这给我留下了几个选择:

  • 使用 WSDL 实用程序生成客户特定的 SOAP 客户端代理,并将客户特定的逻辑放入我的应用程序中
  • 使用 WSDL 实用程序生成客户特定的 SOAP 客户端代理,将客户特定的逻辑放入客户中-特定的 DLL 并预见一个插件系统,应用程序可以在其中以通用方式调用插件
  • 编写一个动态生成 SOAP 调用的通用模块

前 2 个选项在我的情况下并不是真正的替代方案,因为我不想要应用程序中的任何客户特定逻辑或客户特定 DLL。

对我来说,从长远来看,第三个选项是最好的,因为它允许我的顾问同事通过我的脚本语言调用 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 服务的调用,还是这根本行不通?
  • 是否有其他好的方法以动态方式生成 SOAP 调用(使用 .Net)?
  • 或者是自己创建 XML(Soap Envelope)来执行 SOAP 调用的唯一方法?
  • 有机会找到一些我可以继续研究的示例代码吗?

提前致谢, 帕特里克

My application has its own scripting language of which I cannot get rid of (lots of customer-specific scripts written). Now my customers are asking if it would be possible to call a SOAP service from within that scripting language. Of course, the SOAP service that needs to be called will be different for every customer. This leaves me with several options:

  • Use the WSDL utility to generate customer-specific SOAP client proxies and put the customer-specific logic in my application
  • Use the WSDL utility to generate customer-specific SOAP client proxies, put the customer-specific logic in customer-specific DLL's and foresee a plug-in system where the application can call the plug-in in a generic way
  • Write a generic module that dynamically generates the SOAP call

The first 2 options are no real alternative in my case since I don't want any customer-specific logic in the application, or customer-specific DLL's.

For me, the 3rd option is, in the long term, the best since it allows my consultant colleagues to call the SOAP service via my scripting language without doing any customer-specific developments. Dynamically adding functions to my scripting language is not a problem, generating the dynamic SOAP call is.

I started by looking at the output of the WSDL utility. Then I started to remove things until it didn't work anymore. The following piece of code is the one still working:

[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]));
}

The URL of the web service can be dynamically by setting the Url property, but I can't find a way to make the method name dynamic.

Adding a generic method like this still seems to work:

...
[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;

But as soon as I remove the GetPrimeNumbers method, the call doesn't work anymore and reports the following error:

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()

Also, changing the web service name in the SoapDocumentMethodAttribute attribute (e.g. to GetPrimo), gives this same error.

Therefore, my questions:

  • Does it make sense to continue on this path, i.e. looking at the WSDL generated logic trying to 'generalize' the call to a (any) SOAP service or will this simply never work?
  • Are there any other good ways of generating the SOAP call in a dynamic way (using .Net)?
  • Or is the only method to create the XML (Soap Envelope) yourself to do the SOAP call?
  • Any chance of finding some example code that I can continue to work on?

Thanks in advance,
Patrick

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

苍白女子 2024-09-21 00:38:48

您可以在脚本语言中提供一种机制来调用外部 .NET 程序集。您可以使用反射来查找函数并调用它们。与许多应用程序中插件的工作方式类似。

这不仅允许客户调用外部 Web 服务,还可以用于许多其他增强功能。

或者,如果您不想依赖客户编写.NET 程序集,您可以通过询问用户 SOAP 消息名称、参数名称、它们的类型和值、服务 URL 等来自己生成 SOAP 请求。但我认为事实并非如此这将是一条容易的路,我承认我自己从来没有这样做过。

You can give a mechanism in your scripting language to call an external .NET assembly. You can use reflection to find the function and call them. Similar to way the plugins work in many applications.

This will not only allow the customers to call an external web service, but can be used for many other enhancements.

Or if you don't want to rely on your customer writing .NET assemblies, you can generate the SOAP requests yourself by asking the user SOAP message name, parameter names, their type and values, service URL etc. But I think it is not going to be an easy path and I admit that I never have done it myself.

兔小萌 2024-09-21 00:38:48

您可以尝试使用内置编译器动态生成动态代码。
直接从代码生成动态代码(使用诸如 http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx)或直接构建类(看
http://msdn.microsoft.com/en- us/library/system.codedom.compiler.codedomprovider.aspx 为例)

生成代码可以通过多种方式完成:

You could try to generate your dynamic code on the fly using the build in compiler.
Either from code directly (using a code provider like http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx) or by building the class directly (see
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.aspx for an example)

Generating the code can be done in several ways:

逆光飞翔i 2024-09-21 00:38:48

如果不知道脚本语言的功能,这个问题很难回答。

一个想法可能是用另一种语言创建更强大的服务,该服务具有工厂模式来调用相关客户服务并以非客户特定的格式返回脚本信息。

当然,这是假设您首先可以从特定于域的脚本语言调用另一个程序。

也许类似 托管可扩展性框架(简称 MEF) 可以工作也适合你。

Without knowing the capabilities of your scripting language this is difficult to answer.

One idea could be to create a more powerful service in another language that has a factory pattern to call the relevant customers service and return to your script information in a format that is non-customer specific.

Of course this is assuming you could call another program in the first place from your domain specific scripting language.

Maybe something like Managed Extensibility Framework (or MEF for short) could work for you too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文