Die IPUB -Refreme ist eine Bibliothek, die Rastdienste auf sehr einfache Weise konsumiert und nur eine Schnittstelle deklariert und vom IPUB -Team erstellt wird.
Sie müssen sich nicht mit String -Manipulation, URL -Codierung, JSON -Manipulation und Kundeneinstellungen befassen. Dies alles wird automatisch von dieser Bibliothek durchgeführt. Sie müssen nur den Kopfball der API -Ruhe deklarieren, der konsumiert wird.
Dieses Projekt inspirierte / basierend auf der vorhandenen Refit in .NET und verwandelt Ihre REST -API in eine Live -Schnittstelle:
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 ;Die Grestservice -Instanz generiert eine Implementierung von Igithubapi, die interne TNethttpclient verwendet, um seine Anrufe zu tätigen:
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));Um die REST -API -Schnittstelle zu deklarieren, gibt es zwei Verpflichtungen:
Die Methoden der REST -API -Schnittstelle können eine Prozedur oder eine Funktion sein, die eine Zeichenfolge, einen Aufzeichnung, eine Klasse, ein dynamisches Array von Datensatz oder dynamischen Klassenarray zurückgibt. Der Methodenname spielt keine Rolle. Um zu deklarieren, sollten Sie ein Attribut deklarieren, das die methodische Art und die relative URL informiert
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
[Post( ' /users/{AUser}?message={AMessage} ' )]
function GetUserJson ( const AUser, AMessage: string): string;Alle Standardmethoden werden unterstützt (Get, Post, Löschen, Put und Patch).
Die relative URL kann Masken {argument_name/property_name} in irgendwohin haben und wiederholen, um zu markieren, wo ein Argument oder eine Eigenschaft eingefügt werden kann. Weitere Details im nächsten Thema.
In Ihrer REST -API -Schnittstelle werden der Namen der Argumente der Methoden verwendet, um die Masken {Argument_Name/Property_Name} in der relativen URL zu ersetzen. In diesem Schritt erlauben wir Fall unempfindliche Namen und Namen ohne den ersten Buchstaben A von Argumentnamen, die häufig in der Delphi -Sprache verwendet werden. Dieser Fälle hat also das gleiche Ergebnis:
[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; Wenn der Argumentname Body , ABody , BodyContent , ABodyContent , Content oder AContent ist, wird das Argument als Körper der Anfrage verwendet. Sie können auch einen anderen Namen deklarieren und das Attribut [Körper] in diesem Argument verwenden. Wenn ein Argument ein Körper ist, unabhängig vom Argumentyp wird es in die Saite gegossen. Wenn es sich um einen Datensatz oder eine Klasse handelt, werden wir sie automatisch mit einem JSON serialisieren. Denken Sie daran, dass die Maske {argument_name/property_name} in der relativen URL in irgendwohin sein kann, einschließlich innerhalb von Abfragen und wiederholen können. Außerdem wird es automatisch codiert, sodass Ihr Argument beispielsweise eine Zeichenfolge mit Leerzeichen sein kann.
Die Art des Arguments spielt keine Rolle, wir werden automatisch an String gegossen. Sie können also beispielsweise einen Ganzzahlparameter verwenden:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;Sie können die Basis -URL optional mithilfe des Attributs [BaseURL ('xxx')] vor der Schnittstelle deklarieren:
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Oder Sie können direkt festlegen, wenn der REST -Dienst eine neue REST -API -Schnittstelle generiert. Dies ist die erste Basis -URL, die der Dienst berücksichtigt:
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );Sie können die erforderlichen Header in der API -Schnittstelle und -Methode deklarieren. Deklare Header, die im gesamten API -Anruf verwendet werden, deklarieren Sie einfach über der Schnittstelle:
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Deklare Header, die in einer API -Methode verwendet werden, deklarieren Sie einfach über die Methode:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;Hinweis: Sie können viele [Header] Attribut in einer Methode oder in einer REST -API -Schnittstelle deklarieren.
Um dynamische Header zu deklarieren, dh je nach Argumenten oder Eigenschaften, können Sie auch die Maske {Argument_Name/Property_Name} für den Header -Wert verwenden. Es gibt jedoch auch eine weitere exklusive Option, um einen Header direkt mit einem Argument zu deklarieren. In diesem letzten Fall müssen Sie das [Header] -attribut in der Parameterdeklaration deklarieren:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Post( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string; [Header( ' Authorization ' )] const AAuthToken: string): string;
end ;In Ihrer REST -API -Schnittstelle können Sie auch jede Art von Eigenschaft deklarieren, solange es zum Lesen und Schreiben ist und Getter und Setter mit demselben Namen wie die Eigenschaft nur durch Hinzufügen von "GET" und "Set" im Voraus haben. Beispiel:
[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 ;Wenn Sie eine Art Authentikatoren wie OAuth1 oder OAuth2 benötigen, können Sie die nativen Komponenten von Delphi wie ToAuth2Authenticator verwenden. In diesem Fall müssen Sie diesen Authentikator selbst erstellen und konfigurieren und im Eigenschaftsauthentikator Ihrer REST -API -Schnittstelle festlegen (es wurde in der übergeordneten Schnittstelle iiPrestapi deklariert)
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 ;HINWEIS: Sie müssen den Authentikator nach dem Gebrauch selbst zerstören. Der REST -Service wird ihn nicht intern tun.
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 ;Die in Ihrer Schnittstelle verwendeten Typen können die JSON -Attribute normal verwenden. Alle Attribute der Einheitssystem.json.Serializer sind zulässig. Beispiel:
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 ;Wenn Sie einen speziellen Typ haben, der einen benutzerdefinierten Konvertit benötigt, können Sie Ihren eigenen JSON -Konverter -Nachkomme des TjsonConverters in System.json.Serializers erstellen und ihn in der US -Bibliothek registrieren:
GRestService.RegisterConverters([TNullableStringConverter]);Sie werden sich nur einmal anmelden, vorzugsweise bei der Initialisierung.
Diese Bibliothek implementiert keine nullbaren Typen, da im Internet verschiedene Implementierungen vorhanden sind. Mehrere Bibliotheken haben bereits ihren eigenen Nullable -Typ. Mit der Möglichkeit, benutzerdefinierte JSON -Konverter zu registrieren, ist es jedoch einfach, jede Art von Nullable -im Code zusammen mit dieser Bibliothek zu implementieren. Hier ein Beispiel eines nullbaren Typs mit dem JSON -Konverter:
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 .Jetzt können Sie das Nullable mit dieser Bibliothek wie folgt verwenden:
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 Es ist wichtig, jede Anfrage zu stornieren, die gestellt wird, insbesondere vor dem Schließen des Programms, da das Warten auf die Gesamtausführung einige Sekunden dauern und damit die Schließung des Programms verzögert. Dafür haben wir die CancelRequest -Methode in der API -Basisklasse (iiPrestapi) erstellt und dann einfach anrufen:
LGithubApi.CancelRequest;Dies wird jedoch eine Ausnahme für den laufenden Antrag (EiprestserviceCeled) einbringen. Weitere Informationen zu nächsten Themen.
Wir haben fünf Ausnahmen, die bei der Nutzung des Dienstes berücksichtigt werden müssen
Um die Ausnahmen zu stillen, sehen Sie das nächste Thema.
Um einige Ausnahmen zu "zum Schweigen", können Sie in Ihrer REST -API -Schnittstelle "Versuchsfunktionen" deklarieren. Ist sehr einfach, deklarieren Sie nur Funktionen, die mit dem Namen "Try" beginnt und boolean zurückgeben. Wenn die Methode ein Ergebnis hat, können Sie sie auch im Parameter mit der Flag "Out" deklarieren. Siehe die gleichen zwei mit und ohne "Versuchsfunktion" deklarierten Methoden:
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 ;Jetzt "wird der Trygetuser die Ausnahmen zum Schweigen bringen" EiprestServicecanceled, EiprestServiceFailed, EiprestServicejson und EiprestServicestatuscode.
Der Grestservice und die von IT erstellten REST -API -Schnittstellen sind fadensicher.
Da die Verbindungen synchron sind, besteht das Ideal darin, die API -Funktionen im Hintergrund aufzurufen. Wenn Sie über mehrere Threads verfügen, können Sie auch mehrere REST -API -Schnittstellen für dieselbe API erstellen, jeder hat eine andere Verbindung.
Diese Bibliothek vollständig plattformübergreifend und wurde auf Delphi Sydney 10.4 hergestellt und getestet, aber es wird wahrscheinlich auf früheren Versionen funktionieren, wahrscheinlich in Delphi 10.2 Tokio oder neuer.
Die IPUB -Repit ist unter MIT lizenziert und die Lizenzdatei ist in diesem Ordner enthalten.