Delphi对接WebService大致分两种, 详细如下:
第一种(这种比较常见):
方便看效果, 本地写了一个基于Axis2的WebService(这个就不描述了), 使用Delphi导入wsdl,Delphi代码如下(注释部分都删掉了):
unit CalcuteStringService;
interface
uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;
const
IS_OPTN = $0001;
IS_NLBL = $0004;
IS_REF = $0080;
type
CalcuteStringServicePortType = interface(IInvokable)
['{4AAF990C-CA65-8B5F-D3DF-3719C6BB14C0}']
function plus2String(const x: Single; const y: Single): string; stdcall;
function multiply2String(const x: Single; const y: Single): string; stdcall;
function divide2String(const x: Single; const y: Single): string; stdcall;
function minus2String(const x: Single; const y: Single): string; stdcall;
end;
function GetCalcuteStringServicePortType(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): CalcuteStringServicePortType;
implementation
uses SysUtils;
function GetCalcuteStringServicePortType(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): CalcuteStringServicePortType;
const
defWSDL = 'C:\Users\cwx\Desktop\DelphiXE\CalcuteStringService.wsdl';
defURL = 'http://localhost:8080/axis2/services/CalcuteStringService.CalcuteStringServiceHttpSoap11Endpoint/';
defSvc = 'CalcuteStringService';
defPrt = 'CalcuteStringServiceHttpSoap11Endpoint';
var
RIO: THTTPRIO;
begin
Result := nil;
if (Addr = '') then
begin
if UseWSDL then
Addr := defWSDL
else
Addr := defURL;
end;
if HTTPRIO = nil then
RIO := THTTPRIO.Create(nil)
else
RIO := HTTPRIO;
RIO.HTTPWebNode.UseUTF8InHeader := True;
try
Result := (RIO as CalcuteStringServicePortType);
if UseWSDL then
begin
RIO.WSDLLocation := Addr;
RIO.Service := defSvc;
RIO.Port := defPrt;
end else
RIO.URL := Addr;
finally
if (Result = nil) and (HTTPRIO = nil) then
RIO.Free;
end;
end;
initialization
{ CalcuteStringServicePortType }
InvRegistry.RegisterInterface(TypeInfo(CalcuteStringServicePortType), 'http://svr.cwx.com', 'UTF-8');
InvRegistry.RegisterDefaultSOAPAction(TypeInfo(CalcuteStringServicePortType), 'urn:%operationName%');
InvRegistry.RegisterInvokeOptions(TypeInfo(CalcuteStringServicePortType), ioDocument);
end.
常见的调用方式如下:
procedure TfrmMain.Button5Click(Sender: TObject);
var
fResult: string;
obj: CalcuteStringServicePortType;
begin
obj:= GetCalcuteStringServicePortType;
fResult:= obj.plus2String(1, 3);
ShowMessage(Format('%s', [fResult]));
end;
也可以界面丢一个THTTPRIO控件, name为"HTTPRIO1"
将CalcuteStringService.pas单元的defURL值拷贝到HTTPRIO1的属性URL中,调用如下:
procedure TfrmMain.Button8Click(Sender: TObject);
begin
ShowMessage(Format('%s', [(HTTPRIO1 as CalcuteStringServicePortType).minus2String(10, 3)]));
end;
第二种(这种比较少见):
这种情况是,接口的传进去的参数和返回来的参数都分别揉合成一团, 以下展示部分代码:
首先是接口doc文档提供了如下两个方法:
public string SignIn(out nStaNum,out nThirdType,out nSecret1,out sSecret2);
public string Payment(string sIDNo, int nIDType, int nStaNum, int nOptNum, int nDealerNum,
int nEWalletNum, int nMonDeal,out uint nStaSID);
使用Delphi导入后,和之前描述的第一种有很大不同, 且单元开头有很多的class和TRemotable,以下为部分代码:
unit ThirdWebservice;
interface
uses InvokeRegistry, SOAPHTTPClient, Types, XSBuiltIns;
const
IS_OPTN = $0001;
IS_REF = $0080;
type
...
SecurityHeader = class;
SecurityHeader2 = class;
SignIn = class;
SignInResponse = class;
Payment = class;
PaymentResponse = class;
...
SecurityHeader = class(TSOAPHeader)
private
FThirdType: Integer;
FSecret1: Integer;
FSecret2: WideString;
FSecret2_Specified: boolean;
procedure SetSecret2(Index: Integer; const AWideString: WideString);
function Secret2_Specified(Index: Integer): boolean;
published
property ThirdType: Integer read FThirdType write FThirdType;
property Secret1: Integer read FSecret1 write FSecret1;
property Secret2: WideString Index (IS_OPTN) read FSecret2 write SetSecret2 stored Secret2_Specified;
end;
SecurityHeader2 = class(SecurityHeader)
private
published
end;
SignIn = class(TRemotable)
private
public
constructor Create; override;
published
end;
SignInResponse = class(TRemotable)
private
FSignInResult: WideString;
FSignInResult_Specified: boolean;
Fstanum: Integer;
FThirdType: Integer;
FSecret1: Integer;
FSecret2: WideString;
FSecret2_Specified: boolean;
procedure SetSignInResult(Index: Integer; const AWideString: WideString);
function SignInResult_Specified(Index: Integer): boolean;
procedure SetSecret2(Index: Integer; const AWideString: WideString);
function Secret2_Specified(Index: Integer): boolean;
public
constructor Create; override;
published
property SignInResult: WideString Index (IS_OPTN) read FSignInResult write SetSignInResult stored SignInResult_Specified;
property stanum: Integer read Fstanum write Fstanum;
property ThirdType: Integer read FThirdType write FThirdType;
property Secret1: Integer read FSecret1 write FSecret1;
property Secret2: WideString Index (IS_OPTN) read FSecret2 write SetSecret2 stored Secret2_Specified;
end;
Payment = class(TRemotable)
private
FsIDNo: WideString;
FsIDNo_Specified: boolean;
FnIDType: Integer;
FnStaNum: Integer;
FnOptNum: Integer;
FnDealerNum: Integer;
FnEWalletNum: Integer;
FnMonDeal: Integer;
procedure SetsIDNo(Index: Integer; const AWideString: WideString);
function sIDNo_Specified(Index: Integer): boolean;
public
constructor Create; override;
published
property sIDNo: WideString Index (IS_OPTN) read FsIDNo write SetsIDNo stored sIDNo_Specified;
property nIDType: Integer read FnIDType write FnIDType;
property nStaNum: Integer read FnStaNum write FnStaNum;
property nOptNum: Integer read FnOptNum write FnOptNum;
property nDealerNum: Integer read FnDealerNum write FnDealerNum;
property nEWalletNum: Integer read FnEWalletNum write FnEWalletNum;
property nMonDeal: Integer read FnMonDeal write FnMonDeal;
end;
PaymentResponse = class(TRemotable)
private
FPaymentResult: WideString;
FPaymentResult_Specified: boolean;
FnStaSID: Cardinal;
procedure SetPaymentResult(Index: Integer; const AWideString: WideString);
function PaymentResult_Specified(Index: Integer): boolean;
public
constructor Create; override;
published
property PaymentResult: WideString Index (IS_OPTN) read FPaymentResult write SetPaymentResult stored PaymentResult_Specified;
property nStaSID: Cardinal read FnStaSID write FnStaSID;
end;
...
ThirdWebserviceSoap = interface(IInvokable)
['{B1E30F59-F39F-B343-44CD-83FFFF665CFA}']
...
// Cannot unwrap:
// - More than one strictly out element was found
// Headers: SecurityHeader:pIn
function SignIn(const parameters: SignIn): SignInResponse; stdcall;
// Cannot unwrap:
// - More than one strictly out element was found
// Headers: SecurityHeader:pIn
function Payment(const parameters: Payment): PaymentResponse; stdcall;
...
end;
function GetThirdWebserviceSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): ThirdWebserviceSoap;
implementation
uses SysUtils;
...
省略
...
initialization
...
InvRegistry.RegisterInvokeOptions(TypeInfo(ThirdWebserviceSoap), ioDocument);
...
end.
可以看到SignIn方法和Payment方法的参数都成了一个类,而且返回也成了一个类,那么调用的时候,和之前的有所不同.
方法名和参数名的类定义重名,需要注意
SignIn方法的参数parameters是类SignIn类的实例,Sign方法返回的是对象SignInResponse,可以从上面看出这个对象包含了哪些 参数。从接口文档看出,虽然SignIn的参数都是out,那么在调用的时候,SignIn参数类还是要Create一下。调用方式如下:
var
client: ThirdWebserviceSoap;
outParams: SignInResponse;
inParams: SignIn;
begin
inParams:= SignIn.Create; //传进去的参数集
client:= GetThirdWebserviceSoap;
try
outParams:= client.SignIn(inParams); //传出来的参数集
if outParams<> nil then
begin
edt_nstanum.Text:= Format('%d', [outParams.stanum]);
edt_nThirdType.Text:= Format('%d', [outParams.ThirdType]);
edt_nSecret1.Text:= Format('%d', [outParams.Secret1]);
edt_sSecret2.Text:= Format('%s', [outParams.Secret2]);
Memo1.Lines.Add('Over!');
// ShowMessage(Format('%s %d %d %d %s', [outParams.SignInResult, outParams.stanum, outParams.ThirdType, outParams.Secret1, outParams.Secret2]));
end;
finally
client:= nil;
FreeAndNil(inParams);
end;
end;
Payment方法有in进去的参数,又有out回来的参数,调用和上面一样,稍微增加了一点。调用方式如下:
var
client: ThirdWebserviceSoap;
outParams: PaymentResponse;
inParams: Payment;
begin
edt_order.Text:= '';
inParams:= Payment.Create;
inParams.sIDNo:= '4'; //给传递进去的参数赋值
inParams.nIDType:= 4;
inParams.nStaNum:= StrToInt(edt_nstanum.Text);
inParams.nOptNum:= 1005;
inParams.nDealerNum:= 1018;
inParams.nEWalletNum:= 1;
inParams.nMonDeal:= 1;
client:= GetThirdWebserviceSoap;
try
outParams:= client.Payment(inParams);
if outParams<> nil then
begin
edt_order.Text:= Format('%u', [outParams.nStaSID]); //取值
end;
finally
client:= nil;
FreeAndNil(inParams);
end;
end;
其实这个Payment方法是需要安全验证的,信息写在安全头SecurityHeader2中, 在wsdl的生成单元的最下面initialization部分可以看到
InvRegistry.RegisterHeaderClass(TypeInfo(ThirdWebserviceSoap), SecurityHeader2, 'SecurityHeader', '...省略了...');
那么Paymentde调用代码要修改下, 主要是看h, header变量的操作
var
client: ThirdWebserviceSoap;
outParams: PaymentResponse;
inParams: Payment;
h: SecurityHeader;
header: ISOAPHeaders;
code: string;
begin
edt_order.Text:= '';
inParams:= Payment.Create;
h:= SecurityHeader2.Create; //安全头创建
try
code:= Trim(edt_code.Text);
h.ThirdType:= StrToInt(edt_nThirdType.Text);
h.Secret1:= StrToInt(edt_nSecret1.Text);
h.Secret2:= edt_sSecret2.Text;
inParams.sIDNo:= code; //给传递进去的参数赋值
inParams.nIDType:= 4;
inParams.nStaNum:= StrToInt(edt_nstanum.Text);
inParams.nOptNum:= 1005;
inParams.nDealerNum:= 1018;
inParams.nEWalletNum:= 1;
inParams.nMonDeal:= 1;
client:= GetThirdWebserviceSoap;
header:= (client as ISOAPHeaders);
header.Send(h);
outParams:= client.Payment(inParams);
if outParams<> nil then
begin
edt_order.Text:= Format('%u', [outParams.nStaSID]); //取值
Memo1.Lines.Add('Payment code: '+ outParams.PaymentResult);
end;
finally
client:= nil;
FreeAndNil(inParams);
FreeAndNil(h);
end;
end;
完毕!
其次整理了下网上的相关知识:
1. 如果传递的内容存在中文的情况,需要添加 RIO.HTTPWebNode.UseUTF8InHeader := True;
2. initialization部分 合理使用如下两个
InvRegistry.RegisterInvokeOptions(TypeInfo(ThirdWebserviceSoap), ioDocument);
InvRegistry.RegisterInvokeOptions(TypeInfo(ThirdWebserviceSoap), ioLiteral);
再次,Delphi 2007的bin目录下有一个WSDLImp.exe, XE 10.1的bin目录下有WSDLImp.exe和WSDLImp.sym。我单独拷贝出来放在D盘下方便测试。
除了常见的Delphi->New-> Oher-> WSDL Importer, 还可以通过WSDLIml.exe来导入, 回来WSDLIml.exe所在目录直接生成一个pas文件, 操作如下:
结束!