12

我需要发送这样的东西:

   <soapenv:Header>
      <ser:userName>admin</ser:userName>
      <ser:userPassword>secret</ser:userPassword>
   </soapenv:Header>

Delphi WSDL 导入器,生成了这个:

  userName2 = class(TSOAPHeader)
  private
    FValue: string;
  published
    property Value: string  read FValue write FValue;
  end;

  userName      =  type string;

  WsService = interface(IInvokable)
    function call(const userName: userName; const userPassword: userPassword);

并将类型注册为:

  InvRegistry.RegisterHeaderClass(TypeInfo(WsService), userName2, 'userName', 'http://localhost/path/to/services');

问题是,当我使用 delphi 生成的代码调用它时,它会将用户名和密码放在 SOAP 消息的 Body 部分,而不是 Header 中

所以我尝试自己发送标头,如下所示:

将类型定义更改为从 userName2 类继承,因为我无法使用 ISOAPHeaders.Send() 方法发送字符串。

userName = class(userName2);        

然后发送标题:

user := userName.Create;
user.Value := 'admin';

WS := GetWsService;
(WS as ISOAPHeaders).Send(user);

现在标头位于正确的位置,但它们的发送方式如下:

<SOAP-ENV:Header>
    <NS1:userName xmlns:NS1="http://localhost/path/to/services">
        <Value xmlns="http://localhost/path/to/services">admin</Value>
    </NS1:userName>
</SOAP-ENV:Header>

快到了,但我不想要“Value”属性,我只想要一个简单的 header 标记

我该怎么做?

谢谢。

== 编辑 ==

根据要求,WSDL 在这里:http ://desenvolvimento.lemontech.com.br:8081/wsselfbooking/WsSelfBookingService?wsdl

SOAP UI 将其导入并生成此示例请求:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://lemontech.com.br/selfbooking/wsselfbooking/services">
   <soapenv:Header>
      <ser:userPassword></ser:userPassword>
      <ser:userName></ser:userName>
      <ser:keyClient></ser:keyClient>
   </soapenv:Header>
   <soapenv:Body>
      <ser:pesquisarSolicitacao>
         <!--You have a CHOICE of the next 2 items at this level-->
         <idSolicitacaoRef></idSolicitacaoRef>
         <dataInicial></dataInicial>
         <dataFinal></dataFinal>
         <registroInicial>1</registroInicial>
         <!--Optional:-->
         <quantidadeRegistros>50</quantidadeRegistros>
      </ser:pesquisarSolicitacao>
   </soapenv:Body>
</soapenv:Envelope>

这个示例请求工作得很好,但我不知道如何在 Delphi 中进行这个调用。

4

3 回答 3

9

您可以覆盖任何TSOAPHeader类的序列化。只需覆盖其ObjectToSOAP功能。我想出了这个:

unit Unit16;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, WsSelfBookingService, StdCtrls,
  InvokeRegistry, SOAPHTTPClient, opCOnvertOptions, XMLIntf, XSBuiltIns;

type
  TForm1 = class(TForm)
    Memo2: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
 TSOAPCredentials = class(TSoapHeader)
 private
    FPassword: string;
    FUsername: string;
    FKeyClient: string;
 public
   function ObjectToSOAP(RootNode, ParentNode: IXMLNode;
                            const ObjConverter: IObjConverter;
                            const NodeName, NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions;
                            out RefID: InvString): IXMLNode; override;
 published
   property userName     : string read FUsername write Fusername;
   property userPassword : string read FPassword write FPassword;
   property keyClient : string read FKeyClient write FKeyClient;
 end;



var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TSOAPCredentials }

function TSOAPCredentials.ObjectToSOAP(RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const NodeName,
  NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString): IXMLNode;
begin
 Result := ParentNode.AddChild('userName');
 Result.Text := FUsername;
 Result := ParentNode.AddChild('userPassword');
 Result.Text := FPassword;
 Result := ParentNode.AddChild('keyClient');
 Result.Text := FKeyClient;
end;

procedure TForm1.Button1Click(Sender: TObject);

var
 ws   : WsSelfBooking;
 Req  : pesquisarSolicitacao;
 Resp : pesquisarSolicitacaoResponse;
 Rio  : THTTPRIO;
 Cred : TSOAPCredentials;

begin
 Rio := THttpRIO.Create(nil);
 ws := GetWsSelfBooking(false, '', Rio);
 Cred := TSOAPCredentials.Create;
 Cred.userName := 'admin';
 Cred.userPassword := 'secret';
 Cred.keyClient := 'key';
 Rio.SOAPHeaders.Send(cred);
 Req := pesquisarSolicitacao.Create;
 Req.registroInicial := 1;
 Req.quantidadeRegistros := 50;
 Resp := ws.pesquisarSolicitacao(Req);    
end;

end.

导致此请求标头:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
 <SOAP-ENV:userName>admin</SOAP-ENV:userName>
 <SOAP-ENV:userPassword>secret</SOAP-ENV:userPassword>
 <SOAP-ENV:keyClient>key</SOAP-ENV:keyClient>
</SOAP-ENV:Header>
于 2012-12-11T15:16:22.937 回答
8

解决方案:

这不是很明显,但我只需将IS_TEXT值添加到索引并声明一个新的 TSOAPHeader 后代,解决方案是这样的:

const
  IS_TEXT = $0020;

type
  TSimpleHeader = class(TSOAPHeader)
  private
    FValue: string;
  published
    property Value: string Index (IS_TEXT) read FValue write FValue;
  end;

  userName = class(TSimpleHeader);

然后注册这个头文件:

InvRegistry.RegisterHeaderClass(TypeInfo(WsService), userName, 'userName', 'http://localhost/path/to/services');

并手动发送标题:

    User := userName.Create;
    User.Value := 'username';

    (WS as ISOAPHeaders).Send(User);

基本上,索引中的 IS_TEXT 值会阻止 Delphi 在其中创建 userName 标记和 Value 标记。它只是将 Value 属性的字符串放在 userName 标记内。

很遗憾,索引键用于一些不那么明显的东西,关于它的文档也很难找到也很难理解:

AS_ATTRIBUTE 功能已被弃用。它仍然适用于遗留代码,但首选方法是使用属性的索引值。index 属性允许您指定属性是属性、无界元素、可选元素、文本值还是可以具有 NULL 值。

资料来源:http ://docwiki.embarcadero.com/RADStudio/XE3/en/Using_Remotable_Objects

于 2012-12-11T13:51:43.557 回答
2

您可以在 XML 字符串“上线”前使用 stringreplace 来注入标签。您需要一个 RIO_BeforeExecute 处理程序,然后您可以直接处理 SOAPRequest。

于 2012-12-06T19:51:24.110 回答