Поиск по этому блогу

среда, 19 сентября 2012 г.

SOAP с использованием IdHttp

Если необходимо сформировать нестандартный заголовок, если запрос, который формирует Delphi не соответствует тому, который ожидает Web Service, то лучше не замарачиваться с компонентом HttpRio, а посылать запросы с использованием IdHttp из семейства Indy или другой http клиент



Сделать это зачастую гораздо проще, чем с использванием HttpRio, да и контроль над работой приложения больший. Всего то надо: Кинуть на форму компоненты
http: TidHTTP; // клиент http
idckmngr1: TidCookieManager; // менеджер куков
свойства http
object http: TIdHTTP
AllowCookies = True // разрешаем куки
HandleRedirects = True // разрешаем перенаправление (мне понадобилось)
HTTPOptions = [] // по желанию отменяем все опции, в т.ч. Encoding
...
end

Перенаправление полезно, когда вызываемый сервис перебрасывает нас на страницу авторизации, например.
Метод Post в компоненте idHttp перезагружен и на свое усмотрение мы можем отправлять строки (TString) или потоки (Tstream, TIdMultiPartFormDataStream)
function Post(AURL: string; ASource: TIdStrings): string; overload;
function Post(AURL: string; ASource: TIdStream): string; overload;
function Post(AURL: string; ASource: TIdMultiPartFormDataStream): string; overload;
procedure Post(AURL: string; ASource: TIdMultiPartFormDataStream; AResponseContent: TIdStream); overload; procedure Post(AURL: string; ASource: TIdStrings; AResponseContent: TIdStream); overload;
{Post data provided by a stream, this is for submitting data to a server}
procedure Post(AURL: string; ASource, AResponseContent: TIdStream); overload;

Предположим, у нас имеется сервис Web.ServiceLogin.pl, с методом Login,
URL http://127.0.0.1/Web.ServiceLogin.pl
URL WSDL http://127.0.0.1/Web.ServiceLogin.pl?WSDL

Самое время посмотреть как должно выглядеть тело запроса. Можно сформировать его с помощью WSDL Importer'а, а можно непосредственно из WSDL документа, получить его можно по ссылке:
http://сервер/сервис?WSDL


вот часть WSDL документа, описывающая этот метод (Login):
<s:element name="Login">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" name="username" type="s:string" />
<s:element minOccurs="0" name="password" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>

Читая wsdl документ надо уделить внимание той части, которая следует после тега definitions. Она содержит спецификацию пространств

definitions xmlns="..." xmlns:xs="..." name="..." targetNamespace="СХЕМА"

Необходимо сформировать правильный запрос к этому сервису.
1. Http заголовок, который содержит хост/имя сервиса.метод сервиса
'SOAPAction: "хост/Web.ServiceLogin/Login"'
2. Запрос должен соответствовать спецификации (http://www.w3.org/TR/2007/REC-soap12-part0-20070427/)
3. Вызывать метод и передать ей параметры.
Общая структура SOAP запроса состоит из: конверта (<SOAP-ENV:Envelope>) с указанием спецификаций namespace, в конверте заголовок (<SOAP-ENV:Header>) и тело запроса (<SOAP-ENV:Body>)
<?xml version="1.0" encoding="UTF-8"?>
<
SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <
SOAP-ENV:Header>
           Заголовок
        </
SOAP-ENV:Header>
        <SOAP-ENV:Body>
          Тело
        </SOAP-ENV:Body>
</
SOAP-ENV:Envelope>

Наш запрос должен выглядеть
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>

</SOAP-ENV:Header>
<SOAP-ENV:Body>
<s0:Login xmlns:s0="СХЕМА">
<username>юзер</username>
<password>пароль</password>
</s0:Login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Создаем контейнер в котором будет храниться тело SOAP запроса
httpRequest := TstringList.Create; // список строк
Request_Stream := TmemoryStream.Create; // или поток

// этот поток для ответа
Response_Stream := TMemoryStream.Create;

помещаем в него наш запрос
httpRequest.Text:="<SOAP-ENV:Envelope...</SOAP-ENV:Envelope>";
или
Request_Stream.LoadFromFile('Query.xml'); // предварительно создали файл, в который поместили текст нашего запроса

Формируем заголовок, который будет отправлен на сервер
http.Request.Host := {Указываем Host:Port}
http.Request.Connection := 'keep-alive';
http.Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
http.Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
http.ProtocolVersion := pv1_1;
http.Request.ContentType := 'text/xml; charset=utf-8'; // или charset=windows-1251
// Указываем длину запроса
http.Request.ContentLength := Length(httpRequest.Text);//Request_Stream.Size;
// Добавляем к стандартному заголовку 'SOAPAction:...' с именем сервиса и метода который надо вызвать
http.Request.CustomHeaders.Clear;
http.Request.CustomHeaders.Add('SOAPAction: "http://127.0.0.1/Web.ServiceLogin.pl/Login"');


Отправляем запрос
http_asv.Post('http://127.0.0.1/Web.ServiceLogin.pl', httpRequest,Response_Stream);
Сохраняем его в файл
Response_Stream.SaveToFile('Ответ.xml);

Попутно можем отследить заголовки
запроса
memo1.Lines.Add(http.Request.RawHeaders.GetText);
ответа
memo1.Lines.Add(http.Response.RawHeaders.GetText);

Нюанс. При формировании запроса со сложными типами данных надо учитывать, что сам тип данных в ответ не включается.

SOAP ссылки
http://www.w3schools.com/soap/soap_example.asp
http://www.w3.org/TR/2007/REC-soap12-part0-20070427/