题 用于C ++的通用WebService(SOAP)客户端库


我正在寻找一个简单的C ++ WebService客户端库,可以轻松链接到我的应用程序中。

最好这个库:

  • 可用于访问任何SOAP WebService(因此我可以将URL,WebService名称,WebService方法和所有参数作为参数传递给函数调用)
  • 可以在C ++应用程序中静态链接(所以没有DLL)
  • 是免费软件或低成本
  • 可以在我的申请中使用免版税
  • 可以在Web服务中查询其WSDL并返回可用的方法名称,方法的参数及其数据类型

在你们任何人回答.NET之前:去过那里,尝试过。我对.NET的主要反对意见是:

  • 您可以生成代理,但之后不可能在生成的代理代码中更改WebService名称,因为.NET使用反射来检查WebService名称(请参阅 从自己的脚本语言动态调用SOAP服务 关于那个问题我的问题)
  • 动态生成代理类似乎并不总能正常工作

我已经使用谷歌查找这些信息,但我找不到一个。

谢谢

编辑: 为了进一步澄清这一点,我真的想要一些我可以编写这样的代码(或者这种风格的东西):

SoapClient mySoapClient;
mySoapClient.setURL("http://someserver/somewebservice");
mySoapClient.setMethod("DoSomething");
mySoapClient.setParameter(1,"Hello");
mySoapClient.setParameter(2,12345);
mySoapClient.sendRequest();
string result;
mySoapClient.getResult(result);

没有动态代码生成。


12
2017-11-26 13:16


起源




答案:


你看过gSOAP了吗?我认为它将适合您的需求。

http://gsoap2.sourceforge.net/


5
2017-11-26 13:27



看起来gSOAP生成CPP代码。这仍然需要我生成客户特定的CPP代码。我想要的是一个简单的C ++类(或类集),我可以简单地指定URL,Web服务名称,Web服务方法和参数。 - Patrick
我已经对C ++ SOAP接口做了很多研究,gSOAP是最好的。 叹 祝你好运! - Starkey
如果您事先知道WSDL,那么gSOAP可能是最好的。但是,如果每个客户的Web服务名称,方法和参数都不同,我想从配置文件中读取此信息并根据配置文件的内容进行调用,该怎么办? - Patrick
问题是我必须在我自己的脚本语言中进行SOAP调用。我需要将我的脚本语言中的调用转换为真正的SOAP调用,但由于我无法知道客户将在其脚本中执行哪种Web服务调用,因此我无法事先创建C ++代理。 - Patrick
gSOAP有一个GPL许可方案,如果您正在开发商业应用程序,请注意这一点。或者你可以去商业lic。但它不便宜.. - fduff


我找到了一个使用即时生成的程序集的解决方案(我以前无法工作)。起点是 http://refact.blogspot.com/2007_05_01_archive.html

例如。这是使用PeriodicTable Web服务的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.Web.Services;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;

namespace GenericSoapClient
{
class Program
    {
    static void method1()
        {
        Uri uri = new Uri("http://www.webservicex.net/periodictable.asmx?WSDL");
        WebRequest webRequest = WebRequest.Create(uri);
        System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();

        // Get a WSDL
        ServiceDescription sd = ServiceDescription.Read(requestStream);
        string sdName = sd.Services[0].Name;

        // Initialize a service description servImport
        ServiceDescriptionImporter servImport = new ServiceDescriptionImporter();
        servImport.AddServiceDescription(sd, String.Empty, String.Empty);
        servImport.ProtocolName = "Soap";
        servImport.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;

        CodeNamespace nameSpace = new CodeNamespace();
        CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
        codeCompileUnit.Namespaces.Add(nameSpace);

        // Set Warnings

        ServiceDescriptionImportWarnings warnings = servImport.Import(nameSpace, codeCompileUnit);

        if (warnings == 0)
            {
            StringWriter stringWriter =
                 new StringWriter(System.Globalization.CultureInfo.CurrentCulture);

            Microsoft.CSharp.CSharpCodeProvider prov =
              new Microsoft.CSharp.CSharpCodeProvider();

            prov.GenerateCodeFromNamespace(nameSpace,
               stringWriter,
               new CodeGeneratorOptions());

            string[] assemblyReferences =
               new string[2] { "System.Web.Services.dll", "System.Xml.dll" };

            CompilerParameters param = new CompilerParameters(assemblyReferences);

            param.GenerateExecutable = false;
            param.GenerateInMemory = true;
            param.TreatWarningsAsErrors = false;

            param.WarningLevel = 4;

            CompilerResults results = new CompilerResults(new TempFileCollection());
            results = prov.CompileAssemblyFromDom(param, codeCompileUnit);
            Assembly assembly = results.CompiledAssembly;
            Type service = assembly.GetType(sdName);

            //MethodInfo[] methodInfo = service.GetMethods();

            List<string> methods = new List<string>();

            // only find methods of this object type (the one we generated)
            // we don't want inherited members (this type inherited from SoapHttpClientProtocol)
            foreach (MethodInfo minfo in service.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
                {
                methods.Add(minfo.Name);
                Console.WriteLine (minfo.Name + " returns " + minfo.ReturnType.ToString());
                ParameterInfo[] parameters = minfo.GetParameters();
                foreach (ParameterInfo pinfo in parameters)
                    {
                        Console.WriteLine("   " + pinfo.Name + " " + pinfo.ParameterType.ToString());
                    }
                }

            // Create instance of created web service client proxy
            object obj = assembly.CreateInstance(sdName);

            Type type = obj.GetType();

            object[] args0 = new object[] { };
            string result0 = (string)type.InvokeMember(methods[0], BindingFlags.InvokeMethod, null, obj, args0);
            Console.WriteLine(result0);

            object[] args1 = new object[] { "Oxygen" };
            string result1 = (string)type.InvokeMember(methods[1], BindingFlags.InvokeMethod, null, obj, args1);
            Console.WriteLine(result1);
            }
        }
    }
}

在这段代码我明确使用 methods[0] 和 methods[1] 但实际上你会检查方法名称。在这个例子中,我得到元素周期表中所有元素的名称,然后得到氧的原子量。

此示例尚未包含支持代理的逻辑。我仍然需要添加它,但目前,它解决了我最大的问题,即拥有一个通用的SOAP客户端。

编辑:

我知道这段代码是C#,我最初要求的是C ++解决方案,但是这段代码证明它可以在.NET环境中工作(我仍然可以在我的应用程序的有限部分使用),我可能会重写这段代码进入C ++ / .NET,解决了我的问题。


4
2017-11-29 13:23



这是一个好点!你认为它可以移植到C ++ Builder环境吗?或者它依赖于.NET和Reflection? - bluish
@bluish,这真的依赖于.Net。代码使用.Net生成客户端类,然后使用.Net C#编译器对其进行编译,并使用.Net反射来调用类。 - Patrick
虽然我很高兴OP的问题得到了解决,但我相信这个答案根本不能成为这个问题的公认答案,因为它在C#中,而问题是关于C ++的...... - zgyarmati
很棒的解决方案! - Fabio Guerrazzi


  Axis2C: http://axis.apache.org/axis2/c/core/index.html

Axis2C勾选上述大部分内容,请检查静态链接。


0
2017-11-26 16:11



你能发一些例子(或链接到它)吗? - bluish