我正在使用Delphi XE2与相当大的SOAP服务进行通信。我已经成功导入了wsdl,一切正常。但是,我发现自己编写了很多类似的代码。我想有一个调用我的Web服务的通用方法。我也发现现在很难对我的代码进行多线程处理,因为我必须为每种类型的调用编写这么多代码。
作为一个周末程序员,我远远没有掌握德尔福的进出,但我认为我至少对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;
(是的,我知道我应该尝试..最终和免费在那里:-))
然后,代码中的其他地方我需要调用另一种方法:
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)
...
有没有人知道如何简化我对网络服务的调用?
答案 0 :(得分: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;
有一些必要的类型转换,但就我而言,我认为我会非常安全。有没有人对此有任何其他意见/建议?我的意思是,这有效,但可能有办法加强它。
干杯,
丹