Если необходимо сформировать нестандартный заголовок, если запрос, который формирует 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/
Сделать это зачастую гораздо проще, чем с использванием 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/