通过名称动态调用SOAP方法?

15

我正在使用Delphi XE2与一个相当大的SOAP服务通信。我已经成功导入了wsdl,一切正常。然而,我发现自己在写很多类似的代码。我想要一个通用的方法来调用我的web服务。由于我必须为每种类型的调用编写很多代码,所以我也发现很难将我的代码多线程化。

作为一个周末程序员,我离精通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;

所以当我调用Web服务时,例如:

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;

(是的,我知道我应该在那里使用try..finally和Free :-) )

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

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

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


很有趣看看是否有人能想出这样的方法,但我只是编写了包装程序来“隐藏”细节。 - mj2008
3
非常好的首篇帖子。投了赞成票。欢迎来到SO。 - RobertFrank
1个回答

4
很不可思议,我发布了一个问题,自己试了几个星期都没解决,几小时后突然就解决了...我在SO上四处看看,发现这个对我有所帮助:Delphi - Invoke Record method per name

我的情况有点特殊,因为我正在使用与方法本身相同的类名作为参数来调用方法。我还编写了一个简单版本,与公共网络服务通信。如果有人感兴趣,可以在这里获取该版本的代码:http://www.hook.se/delphi/SoapDynamicInvoke.zip。这是一个有点无用的示例,因为只有当网络服务具有许多不同的方法时才相关于动态方法调用。尽管如此,这可能会对某些人有趣 :-)

下面是我为我的网络服务解决此问题的方法。正如我所说,它非常具体,代码可能更加通用,但对我而言有效。

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

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;

有些类型转换是必要的,但在我的情况下,我认为我会比较安全。是否有其他关于此的评论/建议?我的意思是,这样做可以运行,但可能有提升的方法。

干杯,

Dan


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接