delphi - 按名称动态调用 SOAP 方法?

标签 delphi soap delphi-xe2 rtti

我正在使用 Delphi XE2 与相当大的 SOAP 服务进行通信。我已成功导入 wsdl,一切正常。然而,我发现自己写了很多类似的代码。我想要一个调用我的网络服务的通用方法。我还发现很难像现在这样对我的代码进行多线程处理,因为我必须为每种类型的调用编写大量代码。

作为一个周末程序员,我还远远没有掌握 Delphi 的细节,但我想我至少对 RTTI 有一个相当的了解,我相信它必须用来做我想做的事情。

Web 服务有大约 700 种不同的方法,这几乎就是问题所在。从wsdl生成的代码有以下方法:

function  addPhone(const Params: addPhone): addPhoneResponse; stdcall;
function  updatePhone(const Params: updatePhone): updatePhoneResponse; stdcall;
function  getPhone(const Params: getPhone): getPhoneResponse; stdcall;
function  removePhone(const Params: removePhone): removePhoneResponse; stdcall;
function  listPhone(const Params: listPhone): listPhoneResponse; stdcall;
function  addStuff(const Params: addStuff): addStuffResponse; stdcall;
function  updateStuff(const Params: updateStuff): updateStuffResponse; stdcall;
...
... about 700 more of the above

基本上,可以处理大约 700 种不同类型的事物,并且都有针对它们的添加、更新、获取、删除和列表方法。每次调用时,都有一个相应的类用作 SOAP 请求的参数。正如您在上面看到的,响应也有一个相应的类。

这些类看起来像(非常简化):

addStuff = class
  private
    FStuff: string;
  published
    property stuff: string  Index (IS_UNQL) read FStuff write FStuff;
  end;

因此,当我调用网络服务时,我会执行以下操作:

procedure CreateStuff;
var
    req:    addStuff;
    res:    addStuffResponse;
    soap:   MyWebServicePort;
begin
    // Use the function in the wsdl-generated code to create HTTPRIO
    soap := GetMyWebServicePort(false,'',nil);
    // Create Parameter Object
    req := addPhone.Create;
    req.stuff := 'test';
    // Send the SOAP Request
    res := soap.addStuff(req);
end;

(是的,我知道我应该尝试..finally 并在那里也免费:-) )

然后,在代码中的其他地方我需要调用不同的方法:

procedure listStuff;
var
    req:    listStuff;
    res:    listStuffResponse;
    soap:   MyWebServicePort;
begin
    // Use the function in the wsdl-generated code to create HTTPRIO
    soap := GetMyWebServicePort(false,'',nil);
    // Create Parameter Object
    req := listPhone.Create;
    req.stuff := 'test2';
    // Send the SOAP Request
    res := soap.listStuff(req);
end;

由于我知道参数始终是一个类,其名称与我调用的方法等效,因此我希望能够执行类似于下面的元代码的操作,以便动态调用该调用。我想这需要一些 RTTI 魔法,但我一直找不到方法来做到这一点:

procedure soapRequest(Param: Something; var Response: Something);
begin
  soap := GetMyWebServicePort(false,'',nil);
  Response := soap.DynamicInvoke(Param.ClassName, Param);
end

然后我可以做这样的事情:

soapRequest(VarOfTypeAddStuff,VarOfTypeAddStuffResponse)
soapRequest(VarOfTypeListStuff,VarOfTypeListStuffResponse)
...

有人知道如何简化我对网络服务的调用吗?

最佳答案

这真的很奇怪,我在发布了一个我几周来一直试图解决自己的问题后几个小时突然就自己解决了......我受到了环顾四周的启发,并且我发现这个对我一路有帮助:Delphi - Invoke Record method per name

我的场景有些具体,因为我使用与方法本身具有相同类名的参数来调用方法。我还编写了与公共(public)网络服务通信的更简单的版本。如果有人感兴趣,您可以在这里获取该代码:http://www.hook.se/delphi/SoapDynamicInvoke.zip 。这是一个无用的示例,因为仅当 Web 服务具有许多不同的方法时,进行动态方法调用才有意义。尽管如此,某些人可能会感兴趣:-)

下面是我如何为我的网络服务解决这个问题。如前所述,它非常具体,代码可以变得更通用,但这对我有用。

使用 TRemotable 对象调用此方法,然后使用与该对象的类名同名的方法调用 Web 服务。

function soapRequest(Param: TRemotable): TValue;
var
  soap: AXLPort;
  C: TRttiContext;
  T: TRttiType;
  M: TRttiMethod;
  SoapParam: TArray<TValue>;
  TVres: TValue;
  soap: MyWebServicePort;
begin
  // Use the function in the wsdl-generated code to create HTTPRIO
  soap := GetMyWebServicePort(false,'',nil);  C := TRttiContext.Create;
  T := C.FindType('MyWebService.MyWebServicePort');
  M := T.GetMethod(Param.ClassName);
  SetLength(SoapParam,1);
  SoapParam[0] := TValue.From(Param);
  TVres := M.Invoke(TValue.From<IInterface>(soap), SoapParam);
  Result := TVres;
end;

并使用上面的函数:

procedure DoSomeSoapCalls(Sender: TObject);
var
  req1: getStuff
  res1: getStuffResponse;
  req2: addStuff;
  res2: addStuffResponse;
  res:  TValue;
begin
  //Request #1
  req1 := getStuff.Create;
  req1.stuffToGet := 'abc';
  try
    res := soapRequest(req1);
    res1 := getStuffResponse(res.AsObject);
  finally
    req1.Free;
  end;
  Writeln(res1.someproperty);
  FreeAndNil(res1);

  //Request #2
  req2 := addStuff.Create;
  req2.StuffToAdd := 'cde';
  try
    res := soapRequest(req2);
    res2 := addStuffResponse(res.AsObject);
  finally
    req2.Free;
  end;
  Writeln(res2.result);
  FreeAndNil(res2);
end;

有必要进行一些类型转换,但就我而言,我认为这样做会非常安全。还有人对此有任何其他意见/建议吗?我的意思是,这可行,但可能有方法可以增强它。

干杯,

关于delphi - 按名称动态调用 SOAP 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11247619/

相关文章:

delphi - 使用 Delphi 进行 Unicode 预组合和分解

德尔福XE2 : Is it possible to create Mac GUI applications without FireMonkey?

delphi - 是否可以安装同一个delphi服务应用程序的多个实例?

node.js - Delphi 使用 websockets 连接到 NodeJs

delphi - 找不到“Delphi 7和Microsoft Access 2013”​​提供程序。它可能未正确安装。”

windows - WIN7下防止hard reset删除注册表修改?

java - Apache Camel Spring-WS 路由未初始化

java - XML SOAP 信封在 JAXB 中变为空

带有 wsdl 文件的 Java SOAP

delphi - 新的 Delphi XE2 自动生成的内部版本号是否链接到 1.1.2000 00 :00:00?