Le Refit IPUB est une bibliothèque pour consommer des services de repos d'une manière très simple, déclarant une seule interface et est créée par l'équipe IPUB.
Vous n'avez pas à faire face à la manipulation des chaînes, à l'encodage d'URL, à la manipulation JSON, aux paramètres du client. Tout cela se fait automatiquement par cette bibliothèque, il vous suffit de déclarer l'en-tête de l'API REST qui sera consommé.
Ce projet a inspiré / basé sur la remise en forme existante dans .NET, et il transforme votre API REST en une interface en direct:
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 ;L'instance GrestService génère une implémentation d'IGitHubAPI qui utilise en interne tnethttpclient pour passer ses appels:
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));Pour déclarer l'interface API REST, il y a deux obligations:
Les méthodes de l'interface API REST peuvent être une procédure ou une fonction renvoyant une chaîne, un enregistrement, une classe, un tableau dynamique d'enregistrement ou un tableau de classe dynamique. Le nom de la méthode n'a pas d'importance. Pour déclarer que vous devez déclarer un attribut informant le type de méthode et l'URL relative, vous devez déclarer le type de méthode de la méthode d'interface et l'URL relative
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
[Post( ' /users/{AUser}?message={AMessage} ' )]
function GetUserJson ( const AUser, AMessage: string): string;Toutes les méthodes standard sont prises en charge (obtenir, publier, supprimer, mettre et patcher).
L'URL relative peut avoir des masques {argument_name / propriété_name}, dans n'importe où et peut répéter, pour marquer lorsqu'un argument ou une propriété peut être inséré. Plus de détails dans le sujet suivant.
Dans votre interface API REST, le nom des arguments des méthodes sera utilisé pour remplacer les masques {argument_name / propriété_name} dans URL relative. Dans cette étape, nous permettons des noms et des noms insensibles de cas sans la première lettre A des noms d'argument utilisés généralement dans le langage Delphi. Ainsi, ces cas auront le même résultat:
[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; Si le nom de l'argument est Body , ABody , BodyContent , le ABodyContent , Content ou AContent , l'argument sera utilisé comme corps de la demande. Vous pouvez également déclarer un autre nom et utiliser l'attribut [Body] dans cet argument. Lorsqu'un argument est un corps, quel que soit le type d'argument, il sera jeté en chaîne. S'il s'agit d'un enregistrement ou d'une classe, nous le sérialiserons automatiquement en JSON. N'oubliez pas que le masque {argument_name / propriété_name} dans l'URL relative, peut être dans n'importe où, y compris les requêtes à l'intérieur, et peut se répéter. De plus, il sera automatiquement codé, donc votre argument peut être une chaîne avec des espaces, par exemple.
Le type de l'argument n'a pas d'importance, nous allons lancer automatiquement en chaîne. Vous pouvez donc utiliser, par exemple, un paramètre entier:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;Vous pouvez déclarer éventuellement l'URL de base à l'aide de l'attribut [BUSURL ('xxx')] avant l'interface:
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Ou vous pouvez définir directement lorsque le service de repos générera une nouvelle interface API REST, il s'agit de la première URL de base que le service envisagera:
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );Vous pouvez déclarer les en-têtes nécessaires dans l'interface et la méthode de l'API. Aux en-têtes de déclaration qui seront utilisés dans tous les appels API, déclarez simplement au-dessus de l'interface:
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Aux en-têtes de déclaration qui seront utilisés dans une méthode API, déclarez simplement au-dessus de la méthode:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;Remarque: vous pouvez déclarer de nombreux attributs [en-têtes] dans une méthode ou dans une interface API REST.
Mais pour déclarer des en-têtes dynamiques, c'est-à-dire en fonction des arguments ou des propriétés, vous pouvez également utiliser le masque {argument_name / propriété_name} sur la valeur d'en-tête. Mais il existe également une autre option exclusive pour déclarer un en-tête en utilisant directement un argument. Dans ce dernier cas, vous devrez déclarer l'attribut [d'en-tête] dans la déclaration de paramètres:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Post( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string; [Header( ' Authorization ' )] const AAuthToken: string): string;
end ;Dans votre interface API REST, vous pouvez également déclarer n'importe quel type de propriété, tant qu'elle est pour la lecture et l'écriture et a Getter et Setter avec le même nom que la propriété simplement en ajoutant "Get" et "Set" au préalable. Exemple:
[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 ;Lorsque vous avez besoin d'une sorte d'authentificateur comme OAuth1 ou OAuth2, vous pouvez utiliser les composants natifs de Delphi comme ToAuth2Authenticator. Dans ce cas, vous devrez créer et configurer cet authentificateur par vous-même et le définir dans l'authentificateur de propriétés de votre interface API REST (il a été déclaré dans l'interface parent, 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 ;Remarque: vous devez détruire l'authentificateur par vous-même après l'utiliser, le service de repos ne le fera pas en interne.
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 ;Les types utilisés dans votre interface peuvent utiliser normalement les attributs JSON. Tous les attributs du système unitaire.json.Serialiseurs sont autorisés. Exemple:
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 ;Si vous avez un type spécial, qui a besoin d'un converti personnalisé, vous pouvez créer votre propre convertisseur JSON descendant du tjsonconverter dans System.json.serializers et l'enregistrez-le dans la bibliothèque américaine:
GRestService.RegisterConverters([TNullableStringConverter]);Vous vous inscrirez une seule fois, de préférence à l'initialisation.
Cette bibliothèque n'implémente pas les types nullables car il existe plusieurs implémentations différentes sur Internet, plusieurs bibliothèques ont déjà leur propre type nullable. Mais avec la possibilité d'enregistrer des convertisseurs JSON personnalisés, il est facile d'implémenter tout type de nullable dans le code à utiliser avec cette bibliothèque. Ici, un exemple d'un type nullable avec le convertisseur 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 .Maintenant, vous pouvez utiliser le nullable avec cette bibliothèque comme:
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 Il est important de pouvoir annuler toute demande qui est faite, surtout avant de clôturer le programme, car l'attente de l'exécution totale peut prendre quelques secondes et ainsi retarder la fermeture du programme. Pour cela, nous avons créé la méthode AnnuleRquest dans la classe de base API (IIPRESTAPI), puis il suffit d'appeler:
LGithubApi.CancelRequest;Mais cela soulèvera une exception pour la demande en cours (eiprestServiceCanceled). Plus d'informations dans les sujets suivants.
Nous avons cinq exceptions qui doivent être prises en compte lors de l'utilisation du service
Pour "silence" les exceptions, consultez le sujet suivant.
Pour "silence" quelques exceptions, vous pouvez déclarer "Essayez les fonctions" dans votre interface API REST. Est très simple, il suffit de déclarer des fonctions qui commencent par le nom "essayez" et renvoie booléen. Si la méthode a un résultat, vous pouvez également le déclarer en paramètre avec l'indicateur "Out". Voir les deux mêmes méthodes déclarées avec et sans "Fonction d'essayer":
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 ;Maintenant, le TryGetUser "réticulera" les exceptions eiprestServiceCanceled, eiprestServicefailed, eiprestServicejson et eiprestServiceStatuscode.
Les interfaces API GrestService et REST créées par lui sont sûres.
Comme les connexions sont synchrones, l'idéal est d'appeler les fonctions de l'API en arrière-plan. Si vous avez plusieurs threads, vous pouvez également créer plusieurs interfaces API REST pour la même API, chacune aura une connexion différente.
Cette bibliothèque complète multiplateforme et a été fabriquée et testée sur Delphi Sydney 10.4, mais il est susceptible de fonctionner sur les versions précédentes, probablement dans Delphi 10.2 Tokyo ou plus récente.
Le Refit IPUB est sous licence en vertu du MIT et le fichier de licence est inclus dans ce dossier.