Логично. Но тема не раскрыта.
Во первых, я написал, о том что мне было НУЖЕН json, потому ответы не десереализуются в объекты. Во вторых нет необходимости использовать TJson.JsonToObject. Все гораздо проще. Как я уже писал, нужно изменить функцию Request. Например так:
function Request<T: class>(ABody: TApiObject): T;
...
function TRestAPI.Request<T>(ABody: TApiObject): T;
var
i: integer;
begin
RESTRequest.ClearBody;
RESTRequest.AddBody(ABody);
RESTRequest.Execute;
if (RESTResponse.StatusCode <> 200) then
raise Exception.CreateFmt('Оштбка сервера %d "%s"',[RESTResponse.StatusCode, RESTResponse.StatusText]);
if not Assigned(RESTResponse.JSONValue) then
raise Exception.Create('Ответ сервера не содержит подходящих данных');
Result := RESTResponse.JSONValue.GetValue<T>('content');
end;
Теперь по поводу потоков. Все верно, но не совсем. Поскольку TRestAPI это наследник TDataModule, то я подразумевал использование в программе единственного его экземпляра. А значит код на подобии такого - полный отстой:
function TRestAPI.GetUserInfo(ABody: TApiInfo): TJSONValue;
begin
TThread.CreateAnonymousThread(procedure
begin
RESTClient.BaseURL := BaseURL + '?act=terminal&area=info&infoType=2&response=js';
Result := Request(ABody);
end).Start;
end;
Почему? Потому что если из главного модуля выполнить подряд сразу два запроса - то мы получим exception. Так как потоки раздерутся из-за единственных экземпляров:
RESTClient: TRESTClient;
RESTRequest: TRESTRequest;
RESTResponse: TRESTResponse;
Первое что приходит на ум:
1. Создавать динамически TRestAPI для каждого запроса
2. Создавать TRESTClient, TRESTRequest, TRESTResponse для каждого запроса.
Логично? Нет. Это вообще полное гамно плохая идея. Потому что будет много лишнего кода, а мы стремимся все сделать красиво.
Что же, мы к этому подошли, покажу как же я использовал данный код. А использовал я его конечно же в потоках.
Идея очень простая и весьма удобная. Взял я ее с сайта fmxexpress.com (к сожалению не помню автора, но вы можете найти). Мне она настолько понравилась, что я теперь использую ее повсеместно. Конечно код я модернизировал под себя и адаптировал для использования на desktop.
Код позволяет отправить в поток любую функцию. При этом при завершении или ошибке будет вызваны соответсвующие функции. Но весь код будет находится в одном месте (используются анонимные методы). При выполнении запроса будет выведено окно с анимацией и надписью "Ждите..."
Вот пример вызова запроса:
procedure TFormCashbox.RequestCardInfo(const ACardNum: string);
var
Json: TJSONValue;
Body: TApiInfo;
begin
lblStatus.Text := 'Запрос информации...';
Wait.Run(
procedure(AWait: TWaitControl)
begin
Body := TApiInfo.Create;
Body.Card_num := ACardNum;
Body.Hall_id := '1600';
Json := RestAPI.SendRequest(Body);
end,
procedure(AWait: TWaitControl)
begin
CurrentCard.SetState(TCardState.Used, ACardNum, Json);
lblStatus.Text := 'Готов';
end,
procedure (AWait: TWaitControl; AException: Exception)
begin
OnException(Self, AException);
end
);
end;
Весь секрет кроется в Wait. Метод Run принимает три метода:
1.основной код
2.код завершения потока
3.код при возникновении ошибки в потоке.
Сам же Wait: TWaitControl - это наследник от TRectangle. Когда выполняется метод Run, он появляется на экране и показывает надпись. Дополнительно он поверх всей формы выводит полупрозрачный фон, что создает эффект как на многих сайтах.
Я не буду объяснять как работает TWaitControl, там все тривиально. Просто приложу готовый модуль - пользуйтесь. С помощью него вы можете сильно упростить жизнь при работе с потоками.