Подтвердить что ты не робот

Отправить простые строки в заголовке SOAP в Delphi

Мне нужно отправить что-то вроде этого:

   <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, а не в заголовке.

Итак, я попытался отправить заголовки самостоятельно, например:

Изменено определение типа для наследования класса 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, я просто хочу простой простой тег в заголовке.

Как я могу это сделать?

Спасибо.

== EDIT ==

В соответствии с запросом WSDL находится здесь: http://desenvolvimento.lemontech.com.br:8081/wsselfbooking/WsSelfBookingService?wsdl

Пользовательский интерфейс SOAP импортировал его и сгенерировал этот запрос образца:

<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.

4b9b3361

Ответ 1

Решение:

Это было не очень очевидно, но мне просто нужно было добавить значение 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.

Печально, что ключевое слово Index используется для чего-то столь очевидного, а документацию об этом трудно найти и трудно понять:

Функция AS_ATTRIBUTE устарела. Он по-прежнему работает для устаревшего кода, но предпочтительным подходом является использование значения индекса свойства. Свойство index позволяет указать, является ли свойство атрибутом, неограниченным элементом, необязательным элементом, текстовым значением или может иметь значение NULL.

Источник: http://docwiki.embarcadero.com/RADStudio/XE3/en/Using_Remotable_Objects

Ответ 2

Вы можете переопределить сериализацию для любого класса 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>

Ответ 3

Вы можете вставлять теги с помощью stringreplace в строке XML прямо перед тем, как выйти из двери "на провод". Вам нужен обработчик RIO_BeforeExecute, и вы можете напрямую обращаться к SOAPRequest.