但Delphi中的SOAPHeader只能通过继承TSOAPHeader来实现,但这样就会把派生类的名字添加上去,变成了这样:
1.Delphi给了一个SOAP Header的示例
实现方法是:
1.先定义一个TSOAPHeader的继承类,要在SOAP包中出现的字段以property方式出现
private
FAccNumber: Integer;
FTimeStamp: TXSDateTime;
public
destructor Destroy; override; //用于释放FTimeStamp,别无它用
published
property AccNumber: Integer read FAccNumber write FAccNumber;
property TimeStamp: TXSDateTime read FTimeStamp write FTimeStamp;
end;
2.在initialization中注册
RemClassRegistry.RegisterXSClass(AuthHeader, 'urn:BankAccountIntf', 'AuthHeader');
3.使用:
H: AuthHeader;
svc: IBankAccount;
begin
svc := HTTPRIO1 as IBankAccount;
H := AuthHeader.Create;
try
H.AccNumber := FAuthKey;
H.TimeStamp := DateTimeToXSDateTime(FTimeStamp, True);
if UseHeader then
(svc as ISOAPHeaders).send(H);
svc.some_method(...)
finally
H.Free;
end
2. 我的要求和问题
对于我的程序,服务器要求的Header是这样
与上面的不同是Header下直接是两个元素,没有一个NS1:AuthHeader的外包层
仔细查看了一下Delphi的帮助,在Defining and using SOAP headers一节淘到如下一段
Some Web Services define and use headers that are simple types (such as an integer or string) rather than a complex structure that corresponds to a remotable type. However, Delphi's support for SOAP headers requires that you use a TSOAPHeader descendant to represent header types. You can define header classes for simple types by treating the TSOAPHeader class as a holder class. That is, the TSOAPHeader descendant has a single published property, which is the type of the actual header. To signal that the SOAP representation does not need to include a node for the TSOAPHeader descendant, call the remotable type registry's RegisterSerializeOptions method (after registering the header type) and give your header type an option of xoSimpleTypeWrapper.
从最后一句看,我只要这样定义:
private
FUsername: String;
published
property Username: String read FUsername write FUsername;
end;
然后按如下方式注册
RemClassRegistry.RegisterSerializeOptions(TypeInfo(HdrUsername), [xoSimpleTypeWrapper]);
(Password一项也类似处理)
应该就可以了。
但运行一下发现文档里面说的根本就不对,而是这样:
看样子xoSimpleTypeWrapper根本没有任何作用(不加那一句也是这个结果)
3. 问题的解决
在帮助文件里面来回折腾了半天,看到RegisterSerializeOptions的帮助里提到这样一个选项
The remotable object corresponds to a "holder" class. That is, the SOAP representation does not include a node for the class itself, just for its members. This is used when a type that would otherwise not require a remotable class uses a feature only available on remotable classes (such as attributes).
这个选项倒好像跟我的需求比较接近哦,试试!
结果又是失望。“希望,失望,希望,失望”,麦兜抱怨到。
在google上搜索了半天没有发现什么有用的结果,在无望之余回来又来看Delphi提供的例子,注意到它的例子中将AuthHeader这个类注册了一下:
总结一下:
帮助里面说的应该是纯常量作为Header的情况,对于我这种
1. 按上面所说,声明HdrUsername和HdrPassword
2. 在initialization时按如下方式注册
RemClassRegistry.RegisterXSClass(HdrUsername, ', 'Username', 'Username');
RemClassRegistry.RegisterSerializeOptions(TypeInfo(HdrUsername), [xoHolderClass]);
InvRegistry.RegisterHeaderClass(TypeInfo(ezMsgServer), HdrPassword, 'Password', ');
RemClassRegistry.RegisterXSClass(HdrPassword, ', 'Password', 'Password');
RemClassRegistry.RegisterSerializeOptions(HdrPassword, [xoHolderClass]);
其实这样也可以(而且简单一点):
1. 将Username和Password声明在一个Header内
以下内容为程序代码:
private
FUsername: String;
FPassword: String;
published
property Username: String read FUsername write FUsername;
property Password: String read FPassword write FPassword;
end;
2.将TMsgHdr注册:
RemClassRegistry.RegisterXSInfo(TypeInfo(TezMsgHdr), ', 'Username', 'Username');
RemClassRegistry.RegisterSerializeOptions(TypeInfo(TezMsgHdr), [xoSimpleTypeWrapper, xoHolderClass]);
总的来说,还是看E文水平不够啊
4. 其他
细心的话,可以注意到后面一种方法里面多了一个xoSimpleTypeWrapper选项。其实对于后一种方法的复杂类TezMsgHdr, 这个选项是无效的,但对于前一种,就有些不同了(正如其名称所表明的那样,只有真正是SimpleType才有作用):a. 如果只对HdrUsername加xoSimpleTypeWrapper选项:
可以注意到Username元素不见了,只能见到其值y19451赤裸地出现,而Password一项正常
b.如果只对HdrPassword加xoSimpleTypeWrapper选项:
运行时会出现"Element does not contain a single text node"这样的错误。我估计SOAP Header如下,Password的出现导致了这不是一个合法的XML
c.如果两者都加xoSimpleTypeWrapper选项,结果会是怎样呢?
居然Username都不见了