rzaripov.kz

Кросс-платформенный 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-клиент остается единым по логике, но достаточно адаптированным к возможностям каждой платформы.