我正在使用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;
所以当我打电话给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;
(是的,我知道我应该尝试,完全免费,还有:-))
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;
因为我知道这个参数总是一个类,它的名字等同于我所调用的方法,所以我想要像下面的Metacode那样做一些动态的调用.我想这需要一些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服务进行通信.如果有人有兴趣,您可以在这里获得代码:http://www.hook.se/delphi/SoapDynamicInvoke.zip.这是一个无用的例子,因为执行动态方法调用仅在Web服务有很多不同的方法时才相关.不过,有人可能会感兴趣:-)
以下是我如何解决我的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;
有一些打字必要,但在我的情况下,我觉得我会很安全的.有人有任何其他意见/建议吗?我的意思是说,这有用,但可能有办法加强它.
干杯,
担