Кросс-платформенный REST-клиент на Delphi с FireMonkey
Кросс-платформенная разработка позволяет создать единый клиент для Android, iOS, Windows и macOS, сохранив общую бизнес-логику и большую часть интерфейса. Связка Delphi и FireMonkey подходит для такой задачи: визуальные компоненты работают в единой среде, а доступ к серверу строится через HTTP и стандартные REST-инструменты.
Практический REST-клиент должен решать больше задач, чем отправка GET-запроса. Ему требуется безопасно хранить адрес API, сериализовать JSON, обрабатывать авторизацию, различать сетевые и серверные ошибки, корректно обновлять интерфейс и учитывать особенности мобильных платформ. Удачная архитектура заранее разделяет эти обязанности.
Архитектура приложения и границы ответственности
Основу проекта удобно разделить на несколько уровней. Форма FireMonkey отвечает за отображение данных и действия пользователя, отдельный сервисный класс выполняет HTTP-запросы, а модели описывают сущности предметной области. Такой подход не привязывает код работы с API к конкретному экрану и упрощает повторное использование методов.
Например, модуль ApiClient может предоставлять методы GetUsers, CreateOrder или LoadProfile, возвращая готовые объекты или понятный результат операции. Компоненты TRESTClient, TRESTRequest и TRESTResponse допустимо разместить на отдельном невизуальном модуле, однако для сложного приложения лучше создавать запросы в сервисном слое программно. Это дает единый контроль над заголовками, тайм-аутами и обработкой исключений.
При проектировании полезно сразу определить формат результата: данные, код ответа, сообщение об ошибке и технические сведения должны быть отделены друг от друга. Тогда экран сможет показать пользователю понятный текст, а журнал приложения сохранит подробности для диагностики. Четкая структура модулей также положительно влияет на читаемый код Delphi, особенно когда проект развивается несколькими разработчиками.
Подготовка FireMonkey-проекта
В новом приложении FireMonkey следует вынести параметры подключения в отдельный конфигурационный объект. Базовый адрес API, версия, тайм-ауты и режим отладки не должны быть разбросаны по обработчикам кнопок. Для разных сборок можно использовать разные значения: тестовый сервер для разработки и рабочий адрес для выпуска.
В проект добавляются пространства имен REST.Client, REST.Request, REST.Response, System.JSON и, при необходимости, REST.Types. Если запросы выполняются из фонового потока, взаимодействие с компонентами формы нужно выполнять только через TThread.Queue или TThread.Synchronize. FireMonkey, как и другие визуальные библиотеки Delphi, не допускает безопасного изменения элементов интерфейса из произвольного потока.
Для мобильных платформ следует проверить разрешение на доступ к интернету и особенности транспортной безопасности. Android и iOS могут блокировать небезопасные HTTP-соединения, поэтому рабочий API должен использовать HTTPS. Сертификаты, перенаправления и минимально допустимые версии TLS желательно проверить на реальных устройствах, а не только в эмуляторе.
Отправка запросов и работа с JSON
Простой GET-запрос можно построить через стандартные компоненты Delphi. Сначала задается ресурс, затем добавляются параметры и заголовки, после чего вызывается Execute. Для POST и PUT тело запроса обычно формируется как JSON-объект и передается с типом содержимого application/json.
var
Client: TRESTClient;
Request: TRESTRequest;
Response: TRESTResponse;
Body: TJSONObject;
begin
Client := TRESTClient.Create(BaseUrl);
Request := TRESTRequest.Create(nil);
Response := TRESTResponse.Create(nil);
try
Request.Client := Client;
Request.Response := Response;
Request.Method := rmPOST;
Request.Resource := 'users';
Body := TJSONObject.Create;
Body.AddPair('name', UserName);
Body.AddPair('email', UserEmail);
Request.AddBody(Body.ToJSON, ctAPPLICATION_JSON);
Request.Execute;
if Response.StatusCode in [200, 201] then
ProcessUser(Response.JSONValue)
else
RaiseApiError(Response.StatusCode, Response.Content);
finally
Body.Free;
Response.Free;
Request.Free;
Client.Free;
end;
end;
При разборе ответа не стоит обращаться к каждому полю JSON без проверки. Сервер может вернуть null, пропустить необязательное свойство или изменить структуру сообщения об ошибке. Для небольших объектов подойдет TJSONObject, а для повторяющихся моделей полезно написать отдельные методы преобразования, например TUser.FromJson. Это уменьшает количество преобразований внутри экранов и делает формат данных предсказуемым.
Авторизация и сохранение состояния
Для защищенного API чаще всего применяется токен Bearer. После входа сервер возвращает маркер доступа, который клиент добавляет в заголовок Authorization каждого последующего запроса. Формирование этого заголовка лучше сосредоточить в одном месте, чтобы отдельные методы сервиса не могли случайно забыть его.
Срок действия токена необходимо учитывать заранее. При ответе 401 Unauthorized клиент может попытаться обновить токен через refresh-маркер, а затем повторить исходный запрос один раз. Бесконечные повторы недопустимы: при ошибке обновления следует очистить сессию и перевести пользователя на экран авторизации.
Секреты нельзя хранить в исходном коде или в обычном текстовом файле настроек. На Android и iOS для чувствительных данных применяются системные защищенные хранилища, а на Windows и macOS можно использовать платформенные механизмы хранения учетных данных. Даже если FireMonkey скрывает различия платформ, уровень безопасности остается зависимым от конкретной операционной системы.
Ошибки, тайм-ауты и фоновые операции
Сетевой сбой, ошибка DNS, истекший тайм-аут и ответ HTTP 500 имеют разную природу. Пользователю не нужно показывать техническое исключение с именем класса, но приложение должно различать ситуации: нет интернета, сервер временно недоступен, истекла авторизация или введены неверные данные. Для этого удобно создать собственный тип результата или набор исключений API.
Каждый запрос должен иметь ограниченный тайм-аут. Если оставить поведение по умолчанию, мобильный клиент может долго удерживать экран загрузки при нестабильной сети. Во время операции кнопка отправки блокируется, появляется индикатор активности, а после завершения состояние интерфейса восстанавливается независимо от результата запроса.
Долгие операции нельзя выполнять в главном потоке. Подход с TTask.Run позволяет перенести сетевую работу в фон, а TThread.Queue — безопасно вернуть результат в FireMonkey. При закрытии формы нужно отменять или игнорировать завершение операции, иначе обработчик может обратиться к уже уничтоженному визуальному объекту.
Тестирование и переносимость между платформами
REST-клиент следует тестировать отдельно от формы. Сервисные классы можно проверять на тестовом API или на локальном сервере-заглушке, заранее подготавливая ответы для успешного запроса, пустого списка, неверной авторизации и поврежденного JSON. Это помогает обнаружить ошибки сериализации до подключения реального интерфейса.
Полезно проверять также повторную отправку, сортировку, пагинацию и кодировку символов. Русский текст, даты, дробные числа и часовые пояса часто ведут себя по-разному, если сервер и клиент используют неодинаковые правила форматирования. Для дат лучше применять ISO 8601, а числовые значения передавать в JSON без зависимости от региональных настроек устройства.
На Windows и macOS сетевое поведение обычно проще диагностировать, чем на мобильных устройствах. Поэтому итоговые проверки должны проходить на физических Android- и iOS-устройствах через Wi-Fi и мобильную сеть. Нужно учитывать поворот экрана, уход приложения в фон, восстановление соединения и повторное открытие экрана после возврата пользователя. В результате REST-клиент остается единым по логике, но достаточно адаптированным к возможностям каждой платформы.