14

我正在使用 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 种不同类型的东西可以处理,并且它们都有 add、update、get、remove 和 list-methods。对于每个调用,都有一个相应的类用作 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;

(是的,我知道我应该尝试..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 服务的调用?

4

1 回答 1

4

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

我的场景有点具体,因为我使用与方法本身具有相同类名的参数调用方法。我还编写了与公共 Web 服务通信的更简单的版本。如果有人感兴趣,您可以在此处获取该代码: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;

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

干杯,

于 2012-06-28T22:51:29.210 回答