Отправить простые строки в заголовке 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.
Ответы
Ответ 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.