Форум: "Начинающим";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2011.12.18;
Скачать: [xml.tar.bz2];




Вниз

Отправка Soap Headers 


Servy ©   (2011-09-05 13:10) [0]

Возникла необходимость написать клиент к одному SOAP-сервису. От авторов сервиса пришел пример, как должен выглядеть запрос, он приведен ниже. Я заменил url сервиса заказчика на "http://serviceurl/", ибо он длинный и не должен быть доступен широкой публике:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://serviceurl/" xmlns:ns2="redeemVoucher">
 <SOAP-ENV:Header>
  <ns2:Authorization>Basic b2N0b2JlcjoxMjM0</ns2:Authorization>
 </SOAP-ENV:Header>
 <SOAP-ENV:Body>
  <ns1:redeemVoucher>
  <partner_id>26538</partner_id>
  <voucher_id>9910000000980</voucher_id>
  <timeout>20</timeout>
  <transaction_data>Some data here
  </transaction_data>
  </ns1:redeemVoucher>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

В Делфи я экспортнул wsdl"ку. Header в wsdl"ке не описан. Прочитав:

http://docwiki.embarcadero.com/RADStudio/en/Defining_and_Using_SOAP_Headers

Я написал следующий код:

TAuthHeader = class(TSOAPHeader)
private
 FAuthorization: string;
published
 property Authorization: string read FAuthorization write FAuthorization;
end;

// ...

initialization
 { VoucherService }
 InvRegistry.RegisterInterface(TypeInfo(VoucherService), "http://serviceurl/", "UTF-8"); // сгенерировано делфи
 
 InvRegistry.RegisterHeaderClass(TypeInfo(VoucherService), TAuthHeader, "Authentification", "");
 RemClassRegistry.RegisterSerializeOptions(TAuthHeader, [xoSimpleTypeWrapper]);
 
При вызове передаю заголовок следующим образом:

function SendRedeemVoucherW(PartnerId: PWideChar; VoucherId: PWideChar;
                           Timeout: Integer;
                           TransactionData: PWideChar): HRESULT;
var
 Headers: ISOAPHeaders;
 H: TAuthHeader;
begin
 LogWrite("redeemVoucher has been called", Standart);

 Headers := PartnerVoucherService as ISOAPHeaders;

 H := TAuthHeader.Create;
 H.Authorization := "Basic b2N0b2JlcjoxMjM0";
 Headers.OwnsSentHeaders := True;
 Headers.Send(H);

 Result := PartnerVoucherService.redeemVoucher(PartnerId, VoucherId,
                                               Timeout, TransactionData);
                                               

При этом, Делфи отправляет следующий запрос (я отформатировал xml, чтобы удобнее было читать):

POST / HTTP/1.1
SOAPAction: ""
Content-Type: text/xml; charset="utf-8"
User-Agent: CodeGear SOAP 1.3
Host: localhost:8333
Content-Length: 652
Connection: Keep-Alive
Cache-Control: no-cache

<?xml version="1.0"?>
<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>
  <NS1:TAuthHeader xmlns:NS1="urn:uVoucher">
   <NS1:Authorization>Basic b2N0b2JlcjoxMjM0</NS1:Authorization>
  </NS1:TAuthHeader>
 </SOAP-ENV:Header>
 <SOAP-ENV:Body>
  <redeemVoucher xmlns="http://services.partnergw.servop.ru/">
   <partner_id xmlns="">26538</partner_id>
   <voucher_id xmlns="">9910000000980</voucher_id>
   <timeout xmlns="">10</timeout>
   <transaction_data xmlns="">Some data</transaction_data>
  </redeemVoucher>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Сервис возвращает ошибку авторизации. Собственно вопрос - как передать soap-header так, как этого ждет сервис заказчика:

 <SOAP-ENV:Header>
  <ns2:Authorization>Basic b2N0b2JlcjoxMjM0</ns2:Authorization>
 </SOAP-ENV:Header>

А не так, как это происходит при использовании вышеприведенного кода, написанного (ну, я старался ^_^) согласно справке:

 <SOAP-ENV:Header>
  <NS1:TAuthHeader xmlns:NS1="urn:uVoucher">
   <NS1:Authorization>Basic b2N0b2JlcjoxMjM0</NS1:Authorization>
  </NS1:TAuthHeader>
 </SOAP-ENV:Header>


Есть злобный вариант - просто открыть сокет и писать туда все руками так, как этого ждет сервис. Но формирование SOAP запроса руками видится мне костылем, и убивает всю "идею" соапа.

P.S. Мои знания технологии SOAP, и тем более особенностей ее реализации в Делфи далеки до идеала.



Servy ©   (2011-09-07 01:07) [1]

На случай, если кто-то найдет этот тред поиском, поделюсь воплощенным решением:

После копания генофонда, выяснилось, что добавлять чужие xmlns в SOAP:Envelope делфи не умеет в принципе, кроме того, нормального способа дописать их туда, не меняя пресловутый генофонд, или не используя ужас вроде StringReplace"а получившегося xml"я, не нашлось. Судя по всему, по спецификации SOAP"а, которую я полностью естесственно не осилил, эти самые xmlns можно писать и по месту, так, как это делает Делфи, то есть например так:


<SOAP-ENV:Envelope ...>
<SOAP-ENV:Body>
 <redeemVoucher xmlns="http://serviceurl">


вмето


<SOAP-ENV:Envelope xmlns:ns1="http://serviceurl/" ...>
<SOAP-ENV:Body>
 <ns1:redeemVoucher>


Соответственно, проблема была преобразована в другую: получить от Делфи заголовок вида:


<SOAP-ENV:Header>
 <Authorization xmlns="redeemVoucher">Basic b2N0b2JlcjoxMjM0</Authorization>
</SOAP-ENV:Header>


Так как с помощью опций и другого мелкого вмешательства этого добиться не удалось, пришлось вмешаться в процесс преобразования объект->xml, и написать свой класс заголовка такого вида:


 TAuthHeader = class(TSOAPHeader)
 private
   FAuthorization: string;
 published
   property Authorization: string read FAuthorization write FAuthorization;

 public
   function   ObjectToSOAP(RootNode, ParentNode: IXMLNode;
                           const ObjConverter: IObjConverter;
                           const NodeName, NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions;
                           out RefID: InvString): IXMLNode; override;

 end;

type
 TSOAPDomConvAccess = class(TSOAPDomConv);

function TAuthHeader.ObjectToSOAP(RootNode, ParentNode: IXMLNode;
 const ObjConverter: IObjConverter; const NodeName, NodeNamespace,
 ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions;
 out RefID: InvString): IXMLNode;
var
 Conv: TSOAPDomConv;
begin
 Result := ParentNode.AddChild("Authorization", "redeemVoucher", False);

 Conv := (ObjConverter as TSOAPDomConv);
 RefId := TSOAPDomConvAccess(Conv).GetNewID;

 Result.Text := FAuthorization;
end;


В результате добавления заголовка такого класса, генерируется требуемый xml, без двойного тега в SOAP-header и с нужным неймспейсом, и сервис на другом конце это дело успешно ест.



Плохиш ©   (2011-09-07 11:32) [2]

Стоит разместить это на http://edn.embarcadero.com/




Форум: "Начинающим";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2011.12.18;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.75 MB
Время: 0.021 c
15-1314907012     Knight                2011-09-01 23:56  2011.12.18  
Инсталляция программ Windows XP


2-1315465617      user1987              2011-09-08 11:06  2011.12.18  
Вывод текста на рабочий стол


2-1315223947      vasiliy87             2011-09-05 15:59  2011.12.18  
Вопрос о параметрах интефейсных функций


15-1314476996     Юрий                  2011-08-28 00:29  2011.12.18  
С днем рождения ! 28 августа 2011 воскресенье


15-1314544353     alexdn                2011-08-28 19:12  2011.12.18  
Переименовать файл в винде