REST-клиент на Delphi для PHP-бэкенда
Связка Delphi и PHP подходит для приложений, которым требуется единый серверный API и нативные клиенты для разных платформ. PHP отвечает за бизнес-логику, работу с базой данных и авторизацию, а приложение на Delphi отправляет HTTP-запросы и отображает полученные данные.
В качестве формата обмена удобно использовать JSON. Он поддерживается PHP без сторонних расширений, легко читается человеком и напрямую преобразуется в объекты Delphi через System.JSON. Для сетевого взаимодействия в Delphi применяются компоненты TRESTClient, TRESTRequest и TRESTResponse из библиотеки REST Client.
Ниже рассмотрен практический вариант REST-интеграции: от создания PHP-метода до обработки ответа, ошибок, токена доступа и передачи параметров из Delphi. Такой подход подходит для FireMonkey-приложений под Windows, macOS, Linux, Android и iOS.
Подготовка PHP API
Серверный метод должен возвращать предсказуемую структуру JSON и корректный HTTP-статус. Например, PHP-обработчик может принять идентификатор пользователя и вернуть список его задач:
<?php
header('Content-Type: application/json; charset=utf-8');
$userId = filter_input(INPUT_GET, 'user_id', FILTER_VALIDATE_INT);
if (!$userId) {
http_response_code(400);
echo json_encode([
'success' => false,
'error' => 'Некорректный идентификатор пользователя'
], JSON_UNESCAPED_UNICODE);
exit;
}
$tasks = [
['id' => 1, 'title' => 'Проверить API', 'completed' => false],
['id' => 2, 'title' => 'Обновить клиент', 'completed' => true]
];
echo json_encode([
'success' => true,
'data' => $tasks
], JSON_UNESCAPED_UNICODE);
Клиенту важно знать базовый адрес сервиса, маршрут ресурса, доступные HTTP-методы и схему ответа. В данном примере запрос будет выглядеть так: https://example.com/api/tasks.php?user_id=15. Для реального проекта адреса, ключи и параметры подключения лучше хранить в конфигурации, а не жестко записывать в нескольких формах приложения.
PHP API должен использовать коды 200 для успешного ответа, 400 для ошибки параметров, 401 для отсутствия авторизации, 404 для неизвестного ресурса и 500 для внутренней ошибки. Это позволяет Delphi отличать сетевой сбой от ошибки бизнес-логики.
Настройка компонентов REST в Delphi
Создайте форму или отдельный модуль данных и добавьте компоненты TRESTClient, TRESTRequest и TRESTResponse. В свойстве BaseURL клиента указывается общий адрес сервера, а в Resource запроса — относительный путь конкретного метода.
uses
System.SysUtils, System.Classes, System.JSON,
REST.Client, REST.Types, Data.Bind.Components,
IPPeerClient;
type
TApiModule = class(TDataModule)
RESTClient: TRESTClient;
RESTRequest: TRESTRequest;
RESTResponse: TRESTResponse;
public
function LoadTasks(const AUserId: Integer): TJSONArray;
end;
Свяжите компоненты между собой: свойство RESTRequest.Client должно ссылаться на RESTClient, а RESTRequest.Response — на RESTResponse. Визуально это можно сделать через инспектор объектов, а программно — в конструкторе модуля.
procedure TApiModule.DataModuleCreate(Sender: TObject);
begin
RESTClient.BaseURL := 'https://example.com/api';
RESTRequest.Client := RESTClient;
RESTRequest.Response := RESTResponse;
RESTRequest.Resource := 'tasks.php';
RESTRequest.Method := rmGET;
end;
Для мобильных платформ необходимо проверить разрешение на доступ к интернету. В Android-проекте FireMonkey добавляется разрешение INTERNET, а для iOS нужно учитывать ограничения App Transport Security. При использовании HTTPS с действительным сертификатом стандартный REST-клиент обычно работает без дополнительной настройки.
Передача параметров и чтение JSON
Параметры GET-запроса добавляются через AddParameter. Значение будет корректно включено в URL, а специальные символы обработает компонент REST-запроса.
function TApiModule.LoadTasks(const AUserId: Integer): TJSONArray;
var
Root: TJSONObject;
DataValue: TJSONValue;
begin
RESTRequest.Params.Clear;
RESTRequest.AddParameter('user_id', AUserId.ToString,
pkGETorPOST, [poDoNotEncode]);
RESTRequest.Execute;
if RESTResponse.StatusCode <> 200 then
raise Exception.CreateFmt(
'Сервер вернул код %d: %s',
[RESTResponse.StatusCode, RESTResponse.StatusText]
);
Root := TJSONObject.ParseJSONValue(RESTResponse.Content) as TJSONObject;
if not Assigned(Root) then
raise Exception.Create('Ответ сервера не является корректным JSON');
try
if not Root.GetValue<Boolean>('success') then
raise Exception.Create(Root.GetValue<string>('error'));
DataValue := Root.GetValue('data');
if not Assigned(DataValue) or not (DataValue is TJSONArray) then
raise Exception.Create('В ответе отсутствует массив data');
Result := TJSONArray.ParseJSONValue(DataValue.ToJSON) as TJSONArray;
finally
Root.Free;
end;
end;
При разборе JSON следует проверять наличие обязательных полей. В рабочем приложении полезно учитывать null, пустые массивы и изменение версии API. Если сервер возвращает список объектов, каждый элемент можно преобразовать в собственный класс или запись Delphi.
Получение данных не должно выполняться в главном потоке интерфейса. Синхронный вызов Execute на форме временно блокирует окно, поэтому для мобильного или графического приложения лучше использовать TTask.Run, отдельный поток либо асинхронный механизм библиотеки. Обновление компонентов интерфейса после завершения запроса выполняется через TThread.Queue.
Выбор способа сетевого обмена
Для небольшого проекта достаточно стандартных REST-компонентов Delphi. Если требуется единая модель данных, сложная авторизация или большое количество методов, можно дополнить клиент собственными классами-обертками. Они скрывают настройку заголовков и преобразование ответа от экранов приложения.
| Задача | Подход | Когда использовать |
|---|---|---|
| Простой GET-запрос | TRESTRequest.Execute |
Небольшой служебный метод |
| Передача JSON в POST | AddBody с TJSONObject |
Создание или изменение ресурса |
| Повторное использование настроек | Отдельный модуль API | Несколько экранов и форм |
| Долгая операция | TTask или поток |
Мобильный и графический интерфейс |
| Авторизация | HTTP-заголовок Authorization |
Защищенные методы |
| Обработка ошибок | Код статуса и поле error |
Диагностика сервера и клиента |
Для POST-запроса создайте JSON-объект и передайте его в тело сообщения:
procedure TApiModule CreateTask(const ATitle: string);
var
Body: TJSONObject;
begin
Body := TJSONObject.Create;
try
Body.AddPair('title', ATitle);
Body.AddPair('completed', TJSONBool.Create(False));
RESTRequest.Params.Clear;
RESTRequest.Method := rmPOST;
RESTRequest.Resource := 'tasks.php';
RESTRequest.AddBody(Body.ToJSON, ctAPPLICATION_JSON);
RESTRequest.Execute;
finally
Body.Free;
end;
if not (RESTResponse.StatusCode in [200, 201]) then
raise Exception.Create(RESTResponse.Content);
end;
После выполнения POST-запроса сервер может вернуть созданную запись, ее идентификатор или только признак успешной операции. Формат следует заранее закрепить в документации API, чтобы клиент не зависел от случайного текста PHP-скрипта.
Авторизация и защита соединения
Для закрытых методов PHP-бэкенд обычно выдает токен после входа пользователя. Delphi сохраняет его в памяти приложения и добавляет к каждому запросу:
RESTRequest.Params.AddHeader(
'Authorization',
'Bearer ' + AccessToken
);
Токен нельзя передавать в URL, поскольку адрес может попасть в журналы сервера, историю браузера или диагностические сообщения. Обмен учетными данными должен выполняться только по HTTPS. Сам пароль не следует хранить в клиентском приложении или отправлять повторно после получения токена.
На сервере нужно проверять подпись и срок действия токена, права пользователя и допустимость операции. Клиентская проверка интерфейса не является защитой: злоумышленник может отправить запрос напрямую. PHP-приложение обязано самостоятельно валидировать все параметры, ограничивать размер входных данных и использовать подготовленные SQL-запросы.
Обработка сбоев и архитектура клиента
Сетевой вызов может завершиться исключением из-за отсутствия интернета, ошибки DNS, тайм-аута или недоступности сервера. Поэтому Execute следует окружать обработчиком исключений и показывать пользователю понятное сообщение, не раскрывая внутренние детали PHP и SQL.
try
RESTRequest.Execute;
if RESTResponse.StatusCode = 401 then
raise Exception.Create('Сеанс пользователя истек');
if not (RESTResponse.StatusCode in [200, 201, 204]) then
raise Exception.CreateFmt(
'Ошибка API: %s',
[RESTResponse.Content]
);
except
on E: Exception do
LogError(E.Message);
end;
Удобно вынести работу с REST в отдельный класс TApiClient. Формы тогда вызывают методы GetTasks, CreateTask или DeleteTask, не зная деталей URL, заголовков и JSON. Такой слой упрощает тестирование, замену адреса сервера и переход на другую версию API.
Для повторных запросов можно добавить ограниченное число попыток с увеличивающейся задержкой. Повторять безопасно операции чтения, однако автоматический повтор POST способен создать дубликат записи. Серверные методы, изменяющие данные, желательно делать идемпотентными или снабжать уникальным идентификатором операции.
Проверка интеграции и расширение проекта
Тестирование REST-клиента начинается с проверки API отдельно от Delphi. Для этого подходят Postman, Insomnia или командная утилита curl. Нужно проверить правильный JSON, заголовок Content-Type, коды HTTP, поведение при пустых параметрах и ответ при недействительном токене.
В Delphi стоит протестировать успешный сценарий, недоступный сервер, поврежденный JSON, задержку ответа и пустой массив. Логи должны содержать URL без секретных данных, HTTP-код, длительность запроса и техническое описание ошибки. Пароли, токены и содержимое приватных заголовков в журнал записывать нельзя.
После базовой интеграции API можно расширить пагинацией, фильтрацией, сортировкой, загрузкой файлов и обновлением токенов. Для FireMonkey особенно важно сохранять отзывчивость интерфейса и корректно освобождать JSON-объекты. Четкий контракт между PHP и Delphi, единый формат ошибок и отдельный сетевой слой превращают простой HTTP-вызов в устойчивую основу кроссплатформенного приложения.