HTTP-запросы из прикладного кода
Для новых интеграций используйте HttpPostEx и HttpGetEx. В отличие от старых
HttpPost, HttpPostH и HttpGetH, функции с суффиксом Ex возвращают не только
тело ответа, но и HTTP-статус, заголовки и cookies.
Сигнатуры
HttpPostEx(Url, PostData)
HttpPostEx(Url, PostData, Headers)
HttpGetEx(Url)
HttpGetEx(Url, Headers)
Параметры:
Url— полный URL, включаяhttp://илиhttps://;PostData— тело POST-запроса;Headers— необязательныйRecordс заголовками запроса.
Обе функции возвращают Record.
Поля результата
| Поле | Тип | Назначение |
|---|---|---|
StatusCode |
Integer | HTTP-статус ответа |
Code |
Integer | Совместимый псевдоним StatusCode |
Ok |
Boolean | True для кодов от 200 до 299 |
Body |
String | Тело ответа |
Headers |
String | Полный набор заголовков ответа |
SetCookie |
String | Исходные значения заголовков Set-Cookie |
Cookies |
String | Строка cookies для заголовка следующего запроса |
Поля можно читать как свойства или через Item:
code := Response.Code;
code := Response.Item('StatusCode') As Integer;
body := Response.Item('Body') As String;
Для универсального кода и новых полей рекомендуется Item('Имя').
Простой GET
Var
Response: Record;
Begin
Response := HttpGetEx('https://api.example.ru/status') As Record;
If Not Response.Ok Then
Raise(
'HTTP ' + (Response.Code As String) + ': ' +
(Response.Body As String)
);
Result := Response.Body As String;
End;
POST с JSON
Var
Headers: Record;
Response: Record;
RequestBody: String;
Begin
Headers.Item('Content-Type') := 'application/json; charset=utf-8';
Headers.Item('Accept') := 'application/json';
RequestBody := '{"name":"Тест"}';
Response := HttpPostEx(
'https://api.example.ru/items',
ConvertToUTF8(RequestBody),
Headers
) As Record;
If Not Response.Ok Then
Raise(
'POST завершился с кодом ' + (Response.Code As String) +
'. Ответ: ' + (Response.Body As String)
);
Result := Response.Body As String;
End;
ConvertToUTF8 нужен, когда тело формируется из внутренней строки платформы в
Windows-1251, а сервер ожидает UTF-8. Не применяйте ConvertToUTF8 к уже готовому
UTF-8 или к бинарным данным.
HTTP-статусы
Проверка только Code = 200 слишком узкая. Успешными могут быть:
200 OK— обычный успешный ответ;201 Created— объект создан;202 Accepted— запрос принят в обработку;204 No Content— запрос выполнен, тело отсутствует.
Используйте Response.Ok, если интеграция не требует отдельной обработки каждого
успешного статуса.
If Response.Ok Then
Result := Response.Body As String
Else
Raise('HTTP ' + (Response.Code As String));
Когда поведение зависит от статуса, используйте Case:
Case Response.Code Of
200:
Result := Response.Body As String;
201:
Result := Response.Body As String;
401:
Raise('Сервер отклонил авторизацию');
403:
Raise('Недостаточно прав для выполнения запроса');
404:
Raise('Ресурс не найден');
500:
Raise('Внутренняя ошибка сервера');
Else
Raise(
'Неожиданный HTTP-статус ' + (Response.Code As String) +
': ' + (Response.Body As String)
);
End;
Коды 300–399 WinHTTP обычно обрабатывает автоматически. В результате возвращается
статус последнего ответа цепочки. Cookies, полученные на промежуточных ответах перед
редиректом, также собираются в SetCookie и Cookies.
Получение cookies
Сервер возвращает cookie заголовком Set-Cookie:
Set-Cookie: session=abc123; Path=/; HttpOnly
Платформа предоставляет два представления:
SetCookie = session=abc123; Path=/; HttpOnly
Cookies = session=abc123
Для диагностики используйте SetCookie. Для следующего запроса используйте
Cookies.
Response := HttpPostEx(LoginUrl, LoginBody, Headers) As Record;
Warning('Set-Cookie: ' + (Response.Item('SetCookie') As String));
Warning('Cookie: ' + (Response.Item('Cookies') As String));
Повторная отправка cookies
HttpPostEx и HttpGetEx не хранят прикладную сессию между независимыми вызовами.
Приложение должно сохранить cookie и передать её заголовком Cookie.
Var
SessionCookie: String;
Function Login(LoginUrl: String; LoginBody: String): String;
Var
Headers: Record;
Response: Record;
NewCookies: String;
Begin
Headers.Item('Content-Type') := 'application/json; charset=utf-8';
Response := HttpPostEx(LoginUrl, LoginBody, Headers) As Record;
// Cookie может прийти и при статусе, отличном от 200.
NewCookies := Response.Item('Cookies') As String;
If NewCookies <> '' Then
SessionCookie := NewCookies;
If Not Response.Ok Then
Raise(
'Ошибка входа. HTTP ' + (Response.Code As String) +
': ' + (Response.Body As String)
);
Result := Response.Body As String;
End;
Запрос с сохранённой cookie:
Function GetData(DataUrl: String): String;
Var
Headers: Record;
Response: Record;
NewCookies: String;
Begin
Headers.Item('Accept') := 'application/json';
If SessionCookie <> NULL Then
If SessionCookie <> '' Then
Headers.Item('Cookie') := SessionCookie;
Response := HttpGetEx(DataUrl, Headers) As Record;
// Сервер может обновить cookie при любом запросе.
NewCookies := Response.Item('Cookies') As String;
If NewCookies <> '' Then
SessionCookie := NewCookies;
If Not Response.Ok Then
Raise(
'GET завершился с кодом ' + (Response.Code As String) +
': ' + (Response.Body As String)
);
Result := Response.Body As String;
End;
Важно:
- сервер возвращает заголовок
Set-Cookie, клиент отправляетCookie; - в запрос передаётся
Response.Cookies, а неResponse.SetCookie; - не затирайте сохранённую cookie, если новый ответ вернул пустую строку;
- сохраняйте cookie до проверки
Ok, потому что сервер может установить или удалить её при401,403или редиректе; HttpOnlyзапрещает доступ к cookie из JavaScript браузера, но не мешает нативному HTTP-клиенту платформы получить и отправить её.
Диагностика
Временно выведите весь результат:
Warning('Code: ' + (Response.Code As String));
Warning('Headers: ' + (Response.Item('Headers') As String));
Warning('SetCookie: ' + (Response.Item('SetCookie') As String));
Warning('Cookies: ' + (Response.Item('Cookies') As String));
Warning('Body: ' + (Response.Item('Body') As String));
Если сервер не отвечает, не установлено соединение, истёк таймаут или произошла
ошибка TLS, функция выбрасывает исключение. В таком случае Record с HTTP-статусом
не создаётся, потому что HTTP-ответ от сервера не был получен.
HTTP-статус и транспортная ошибка — разные ситуации:
401,404,500— сервер ответил; код доступен вResponse.Code;- timeout, отказ соединения, ошибка DNS/TLS — сервер не дал HTTP-ответа; выполнение прерывается исключением с диагностикой WinHTTP.
Совместимость
Добавление полей StatusCode, Code, Ok, Headers, SetCookie и Cookies не
меняет массив параметров функций и не ломает старые вызовы. Старый код может
продолжать читать только Body, а новый — использовать расширенный результат.