O iPub Recit é uma biblioteca para consumir serviços de repouso de uma maneira muito simples, declarando apenas uma interface e é criada pela equipe do iPub.
Você não precisa lidar com manipulação de string, codificação de URL, manipulação JSON, configurações do cliente. Tudo isso é feito automaticamente por esta biblioteca, você só precisa declarar o cabeçalho do descanso da API que será consumido.
Este projeto inspirou / baseado na reforma existente no .NET e transforma sua API REST em uma interface ao vivo:
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 ;A instância do GrestService gera uma implementação de igithubapi que usa internamente o TNethttpClient para fazer suas chamadas:
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));Para declarar a interface da API REST, há duas obrigações:
Os métodos da interface da API REST podem ser um procedimento ou uma função retornando uma string, registro, classe, matriz dinâmica de registro ou matriz dinâmica de classe. O nome do método não importa. Para declarar que você deve declarar um atributo informando o método e o URL relativo, você deve declarar o método do método da interface e o URL relativo
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
[Post( ' /users/{AUser}?message={AMessage} ' )]
function GetUserJson ( const AUser, AMessage: string): string;Todos os métodos padrão são suportados (Get, Post, Excluir, Put e Patch).
O URL relativo pode ter máscaras {argument_name/propriedade_name}, em qualquer lugar e pode repetir, para marcar onde um argumento ou propriedade pode ser inserido. Mais detalhes no próximo tópico.
Na sua interface da API REST, o nome dos argumentos dos métodos será usado para substituir as máscaras {argument_name/propriedade_name} no URL relativo. Nesta etapa, permitimos nomes e nomes insensíveis ao caso sem a primeira letra A dos nomes de argumentos usados comumente no idioma Delphi. Então, esses casos terão o mesmo resultado:
[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; Se o nome do argumento for Body , ABody , BodyContent , ABodyContent , Content ou AContent , o argumento será usado como corpo da solicitação. Você também pode declarar outro nome e usar o atributo [corpo] nesse argumento. Quando um argumento é um corpo, independentemente do tipo de argumento, ele será lançado para cordas. Se for um registro ou classe, iremos serializar -o para um JSON automaticamente. Lembre -se de que a máscara {argument_name/propriedade_name} em URL relativa pode estar em qualquer lugar, incluindo consultas internas, e pode repetir. Além disso, ele será codificado automaticamente, para que seu argumento possa ser uma string com espaços, por exemplo.
O tipo de argumento não importa, nós lançaremos para cordas automaticamente. Assim, você pode usar, por exemplo, um parâmetro inteiro:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;Você pode declarar opcionalmente o URL base usando o atributo [Baseurl ('xxx')] antes da interface:
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Ou você pode definir diretamente quando o serviço restante gerar uma nova interface de API REST, este é o primeiro URL base que o serviço considerará:
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );Você pode declarar os cabeçalhos necessários na interface e no método da API. Para os cabeçalhos de declaração que serão usados em todas as chamadas da API, basta declarar acima da interface:
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Para os cabeçalhos de declaração que serão usados em um método da API, basta declarar acima do método:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;NOTA: Você pode declarar muitos atributos [cabeçalhos] em um método ou em uma interface de API em uma REST.
Mas, para declarar cabeçalhos dinâmicos, ou seja, dependendo de argumentos ou propriedades, você também pode usar a máscara {argument_name/propriedade_name} no valor do cabeçalho. Mas há também outra opção exclusiva para declarar um cabeçalho usando um argumento diretamente. Neste último caso, você precisará declarar o atributo [cabeçalho] na declaração de parâmetro:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Post( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string; [Header( ' Authorization ' )] const AAuthToken: string): string;
end ;Na sua interface de API de REST, você também pode declarar qualquer tipo de propriedade, desde que seja para ler e escrever e tenha getter e setter com o mesmo nome da propriedade apenas adicionando "Get" e "Set" antes. Exemplo:
[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 ;Quando você precisa de algum tipo de autenticador como OAuth1 ou OAuth2, pode usar os componentes nativos de Delphi como Toauth2authenticator. Nesse caso, você precisará criar e configurar esse autenticador por si mesmo e defini -lo na propriedade Authenticator da sua interface API REST (ela foi declarada na interface dos pais, 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 ;NOTA: Você precisa destruir o autenticador por si mesmo após usá -lo, o serviço restante não o fará internamente.
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 ;Os tipos usados em sua interface podem usar os atributos JSON normalmente. Todos os atributos do Unit System.json.Serializers são permitidos. Exemplo:
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 ;Se você tem um tipo especial, que precisa de um convertido personalizado, pode criar seu próprio conversor JSON Descendente do TJSONCONCERTER em System.json.Serializers e registrá -lo na biblioteca dos EUA:
GRestService.RegisterConverters([TNullableStringConverter]);Você se registrará apenas uma vez, de preferência na inicialização.
Esta biblioteca não implementa tipos anuláveis porque existem várias implementações diferentes na Internet, várias bibliotecas já têm seu próprio tipo anulado. Mas, com a possibilidade de registrar conversores JSON personalizados, é fácil implementar qualquer tipo de anulável no código a ser usado junto com esta biblioteca. Aqui, um exemplo de um tipo anulável com o JSON Converter:
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 .Agora, você pode usar o anulável com esta biblioteca como:
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 É importante poder cancelar qualquer solicitação que esteja sendo feita, especialmente antes de fechar o programa, porque aguardar a execução total pode levar alguns segundos e, assim, adiar o fechamento do programa. Para isso, criamos o método CancelRequest na classe base da API (Iiprestapi) e depois ligue para:
LGithubApi.CancelRequest;Mas isso aumentará uma exceção para a solicitação em andamento (EIPRESTSERVICECANCELED). Mais informações nos próximos tópicos.
Temos cinco exceções que precisam ser consideradas ao usar o serviço
Para "silenciar" as exceções, veja o próximo tópico.
Para "silenciar" algumas exceções que você pode declarar "tente funções" na sua interface de API REST. É muito simples, basta declarar funções que começam com o nome "Tente" e retornam booleanos. Se o método tiver um resultado, você também poderá declará -lo em parâmetro com o sinalizador "Out". Veja os mesmos dois métodos declarados com e sem "Tente função":
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 ;Agora, o TrygetUser "silenciará" as exceções eiPrestServiceCancelled, EiprestServiceFailed, EiprestServicejson e EiprestServicestatuscode.
As interfaces da API GrestService e as REST criadas por ele são seguras de threads.
Como as conexões são síncronas, o ideal é chamar as funções da API em segundo plano. Se você tiver vários threads, também poderá criar várias interfaces de API REST para a mesma API, cada um terá uma conexão diferente.
Esta plataforma cruzada completa da biblioteca e foi feita e testada no Delphi Sydney 10.4, mas é provável que funcione em versões anteriores, provavelmente no Delphi 10.2 Tóquio ou mais recente.
O REFIT IPUB é licenciado no MIT e o arquivo de licença está incluído nesta pasta.