IPUB Refit - это библиотека, которая очень просто употребляет услуги REST, объявляя только один интерфейс и создан командой IPUB.
Вам не нужно иметь дело с манипуляциями с строками, кодировкой URL, манипуляциями с JSON, настройками клиента. Все это сделано автоматически этой библиотекой, вам просто нужно объявить заголовок отдыха API, который будет использоваться.
Этот проект вдохновлен / основан на существующем ремонте в .NET, и он превращает ваш API REST в живой интерфейс:
TUser = record
name : string;
location: string;
id: Integer;
end ;
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Get( ' /users/{aUser} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{aUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;Экземпляр GrestService генерирует реализацию igithubapi, которая внутренне использует tnethttpclient для выполнения своих вызовов:
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));Чтобы объявить интерфейс REST API, есть два обязательства:
Методы интерфейса API REST может быть процедурой или функцией, возвращающей строку, запись, класс, динамический массив записи или динамический массив класса. Имя метода не имеет значения. Чтобы объявить, что вы должны объявить атрибут, информирующий о методе, и относительный URL, вы должны объявить метод метода интерфейса и относительный URL
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
[Post( ' /users/{AUser}?message={AMessage} ' )]
function GetUserJson ( const AUser, AMessage: string): string;Все стандартные методы поддерживаются (get, post, delete, put и patch).
Относительный URL может иметь маски {argery_name/property_name} в любом месте и может повторить, чтобы отметить, где может быть вставлен аргумент или свойство. Более подробная информация в следующей теме.
В вашем интерфейсе API REST аргументы Имя методов будут использоваться для замены масок {argist_name/property_name} в относительном URL. На этом этапе мы разрешаем нечувствительные имена и имена дела без первой буквы A имен аргументов, обычно используемых на языке Delphi. Таким образом, эти случаи будут иметь такой же результат:
[Get( ' /users/{AUser} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{aUser} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{User} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{user} ' )]
function GetUser ( const AUser: string): TUser; Если аргумент называется Body , ABody , BodyContent , ABodyContent , Content или AContent , аргумент будет использоваться в качестве тела запроса. Вы также можете объявить другое имя и использовать атрибут [Body] в этом аргументе. Когда аргумент - это тело, независимо от типа аргумента, он будет подчинен в строку. Если это запись или класс, мы автоматически сериализуем ее на JSON. Помните, что маска {argement_name/property_name} в относительном URL может быть в любом месте, включая внутренние запросы, и может повторить. Кроме того, он будет автоматически кодируется, поэтому, например, ваш аргумент может быть строкой с пробелами.
Тип аргумента не имеет значения, мы будем автоматически привлекать к строке. Таким образом, вы можете использовать, например, целочисленный параметр:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;Вы можете объявить об базовом URL, используя атрибут [baseurl ('xxx')] перед интерфейсом:
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Или вы можете установить непосредственно, когда служба REST генерирует новый интерфейс API REST, это первый базовый URL, который рассмотрит служба:
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );Вы можете объявить заголовки, необходимые в интерфейсе API и методе. В заголовки Declare, которые будут использоваться во всех вызовах API, просто заявьте над интерфейсом:
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;В заголовки объявления, которые будут использоваться одним методом API, просто заявьте выше метода:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;Примечание. Вы можете объявить много атрибута [заголовков] в одном методе или в одном интерфейсе API REST.
Но чтобы объявить динамические заголовки, то есть в зависимости от аргументов или свойств, вы также можете использовать маску {argist_name/property_name} на значении заголовка. Но есть также еще один эксклюзивный вариант, чтобы объявить заголовок, используя аргумент напрямую. В этом последнем случае вам нужно будет объявить атрибут [заголовок] в объявлении параметра:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Post( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string; [Header( ' Authorization ' )] const AAuthToken: string): string;
end ;В вашем интерфейсе API REST вы также можете объявить какую -либо свойство, если оно является для чтения и письма, а также имеет Getter и Setter с тем же именем, что и свойство, просто добавив «Get» и «SET» заранее. Пример:
[BaseUrl( ' https://maps.googleapis.com ' )]
IipGoogleGeocoding = interface (IipRestApi)
[ ' {668C5505-F90D-43C0-9545-038A86472666} ' ]
[Get( ' /maps/api/geocode/json?address={AAddress}&key={ApiKey} ' )]
function AddressToGeolocation ( const AAddress: string): string;
[Get( ' /maps/api/geocode/json?latlng={ALatitude},{ALongitude}&key={ApiKey} ' )]
function GeolocationToAddress ( const ALatitude, ALongitude: Double): string;
function GetApiKey : string;
procedure SetApiKey ( const AValue: string);
property ApiKey: string read GetApiKey write SetApiKey;
end ;
procedure TForm1.FormCreate (Sender: TObject);
var
LGoogleGeocodingApi: IipGoogleGeocoding;
LGeolocationResponse: string;
LAddressResponse: string;
begin
LGoogleGeocodingApi := GRestService.& For <IipGoogleGeocoding>;
LGoogleGeocodingApi.ApiKey := ' XXXXXXX ' ;
LGeolocationResponse := LGoogleGeocodingApi.AddressToGeolocation( ' Rua Alexandre Dumas, 1562, Chácara Santo Antônio, São Paulo - SP, Brasil ' );
LAddressResponse := LGoogleGeocodingApi.GeolocationToAddress(- 23.6312393 , - 46.7058503 );
end ;Когда вам понадобится какой -то аутентификатор, такой как OAuth1 или OAuth2, вы можете использовать нативные компоненты Delphi Like ToAuth2Authenticator. В этом случае вам нужно будет создать и настроить этот аутентификатор самостоятельно и установить его в свойство Authenticator вашего интерфейса API Rest (он был объявлен в родительском интерфейсе, iiprestapi)
var
LOAuth2: TOAuth2Authenticator;
LGithubApi: IGithubApi;
begin
LOAuth2 := TOAuth2Authenticator.Create( nil );
try
// Configure the LOAuth2
...
LGithubApi := GRestService.& For <IGithubApi>;
LGithubApi.Authenticator := LOAuth2;
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));
finally
LOAuth2.Free;
end ;
end ;Примечание. Вам нужно уничтожить аутентификатора самостоятельно после использования, служба остальных не сделает это внутри.
uses
iPub.Rtl.Refit;
type
TUser = record
Name : string;
Location: string;
Id: Integer;
end ;
TRepository = record
Name : string;
Full_Name: string;
Fork: Boolean;
Description: string;
end ;
TIssue = record
public
type
TUser = record
Login: string;
Id: Integer;
end ;
TLabel = record
Name : string;
Color: string;
end ;
public
Url: string;
Title: string;
User: TIssue.TUser;
Labels: TArray<TIssue.TLabel>;
State: string;
Body: string;
end ;
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Get( ' /users/{user} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{user}/repos ' )]
function GetUserRepos ( const AUser: string): TArray<TRepository>;
[Get( ' /repos/{repositoryOwner}/{repositoryName}/issues?page={page} ' )]
function GetRepositoryIssues ( const ARepositoryOwner, ARepositoryName: string; APage: Integer = 1 ): TArray<TIssue>;
[Get( ' /repos/{repositoryOwner}/{repositoryName}/issues?page={page}&state=open ' )]
function GetRepositoryIssuesOpen ( const ARepositoryOwner, ARepositoryName: string; APage: Integer = 1 ): TArray<TIssue>;
end ;
procedure TForm1.FormCreate (Sender: TObject);
var
LGithubApi: IGithubApi;
LUser: TUser;
LRepos: TArray<TRepository>;
LIssues: TArray<TIssue>;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
LRepos := LGithubApi.GetUserRepos( ' viniciusfbb ' );
LIssues := LGithubApi.GetRepositoryIssues( ' rails ' , ' rails ' );
LIssues := LGithubApi.GetRepositoryIssuesOpen( ' rails ' , ' rails ' , 2 );
end ;Типы, используемые в вашем интерфейсе, могут обычно использовать атрибуты JSON. Все атрибуты единичной системы.json.serializers разрешены. Пример:
uses
iPub.Rtl.Refit, System.JSON.Serializers;
type
TRepository = record
Name : string;
[JsonName( ' full_name ' )] FullName: string;
Fork: Boolean;
Description: string;
end ;
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Get( ' /users/{user}/repos ' )]
function GetUserRepos ( const AUser: string): TArray<TRepository>;
end ;
procedure TForm1.FormCreate (Sender: TObject);
var
LGithubApi: IGithubApi;
LRepos: TArray<TRepository>;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LRepos := LGithubApi.GetUserRepos( ' viniciusfbb ' );
end ;Если у вас есть специальный тип, который нуждается в пользовательском конверт, вы можете создать свой собственный спутник конвертеров JSON The TJsonConverter в System.json.serializers и зарегистрировать его в библиотеке США:
GRestService.RegisterConverters([TNullableStringConverter]);Вы будете зарегистрироваться только один раз, предпочтительно при инициализации.
Эта библиотека не внедряет нулевые типы, поскольку в Интернете есть несколько различных реализаций, в нескольких библиотеках уже есть свой собственный тип. Но с возможностью регистрации пользовательских конвертеров JSON легко реализовать любой тип нулевого в коде для использования вместе с этой библиотекой. Здесь один пример одного нулевого типа с конвертером JSON:
unit ExampleOfNullables;
interface
uses
System.Rtti, System.TypInfo, System.JSON.Serializers, System.JSON.Readers,
System.JSON.Writers, System.JSON.Types, iPub.Rtl.Refit;
type
TNullable<T> = record
strict private
FIsNotNull: Boolean;
function GetIsNull : Boolean;
procedure SetIsNull (AValue: Boolean);
public
Value : T;
property IsNull: Boolean read GetIsNull write SetIsNull;
end ;
implementation
type
TNullableConverter<T> = class (TJsonConverter)
public
procedure WriteJson ( const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); override;
function ReadJson ( const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue;
const ASerializer: TJsonSerializer): TValue; override;
function CanConvert (ATypeInf: PTypeInfo): Boolean; override;
end ;
{ TNullable<T> }
function TNullable <T>.GetIsNull: Boolean;
begin
Result := not FIsNotNull;
end ;
procedure TNullable <T>.SetIsNull(AValue: Boolean);
begin
FIsNotNull := not AValue;
end ;
{ TNullableConverter<T> }
function TNullableConverter <T>.CanConvert(ATypeInf: PTypeInfo): Boolean;
begin
Result := ATypeInf = TypeInfo(TNullable<T>);
end ;
function TNullableConverter <T>.ReadJson( const AReader: TJsonReader;
ATypeInf: PTypeInfo; const AExistingValue: TValue;
const ASerializer: TJsonSerializer): TValue;
var
LNullable: TNullable<T>;
begin
if AReader.TokenType = TJsonToken.Null then
begin
LNullable.IsNull := True;
LNullable. Value := Default(T);
end
else
begin
LNullable.IsNull := False;
LNullable. Value := AReader. Value .AsType<T>;
end ;
TValue.Make(@LNullable, TypeInfo(TNullable<T>), Result);
end ;
procedure TNullableConverter <T>.WriteJson( const AWriter: TJsonWriter;
const AValue: TValue; const ASerializer: TJsonSerializer);
var
LNullable: TNullable<T>;
LValue: TValue;
begin
LNullable := AValue.AsType<TNullable<T>>;
if LNullable.IsNull then
AWriter.WriteNull
else
begin
TValue.Make(@LNullable. Value , TypeInfo(T), LValue);
AWriter.WriteValue(LValue);
end ;
end ;
initialization
GRestService.RegisterConverters([TNullableConverter<string>,
TNullableConverter<Byte>, TNullableConverter<Word>,
TNullableConverter<Integer>, TNullableConverter<Cardinal>,
TNullableConverter<Single>, TNullableConverter<Double>,
TNullableConverter<Int64>, TNullableConverter<UInt64>,
TNullableConverter<TDateTime>, TNullableConverter<Boolean>,
TNullableConverter<Char>]);
end .Теперь вы можете использовать Nullable с этой библиотекой, например:
uses
iPub.Rtl.Refit, ExampleOfNullables;
type
TUser = record
Name : TNullable<string>;
Location: string;
Id: Integer;
Email: TNullable<string>;
end ;
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Get( ' /users/{user} ' )]
function GetUser ( const AUser: string): TUser;
end ;
// ...
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
// Now you will see that LUser.Name.IsNull = False but the LUser.Email.IsNull = True Важно иметь возможность отменить любой запрос, который выполняется, особенно перед закрытием программы, потому что ожидание общего исполнения может занять несколько секунд и, таким образом, задержать закрытие программы. Для этого мы создали метод CancelRequest в базовом классе API (iiprestapi), затем просто вызовите:
LGithubApi.CancelRequest;Но это поднимет исключение для запроса (EiprestServiceCancell). Больше информации по следующим темам.
У нас есть пять исключений, которые необходимо учитывать при использовании услуги
Чтобы «замолчать» исключения, см. Следующую тему.
Чтобы «замолчать» некоторые исключения, которые вы можете объявить «Попробуйте функции» в вашем интерфейсе API REST. Очень просто, просто объявите функции, которые начинаются с названия «попробуйте» и возвращают логические. Если метод имеет результат, вы также можете объявить его в параметре с флагом «Out». Смотрите те же два метода, объявленные с и без «Function Function»:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Get( ' /users/{user} ' )]
function GetUser ( const AUser: string): TUser;
[Get( ' /users/{user} ' )]
function TryGetUser ( const AUser: string; out AResult: TUser): Boolean;
end ;Теперь TrygetUser будет «замолчать» исключения EiprestServiceCanceled, eiprestServicefailed, EiprestServicejson и EiprestServiceStatuscode.
Верносервисные и остальные интерфейсы API, созданные им, безопасны.
Поскольку соединения синхронны, идеал состоит в том, чтобы вызвать функции API в фоновом режиме. Если у вас есть несколько потоков, вы также можете создать несколько интерфейсов API REST для одного и того же API, у каждого из них будет другое соединение.
Эта библиотека полная кросс-платформ и была сделана и протестирована на Delphi Sydney 10.4, но, вероятно, будет работать на предыдущих версиях, вероятно, в Delphi 10.2 Tokyo или Newer.
Переоборудование IPUB лицензируется в соответствии с MIT, а файл лицензии включен в эту папку.