IPUB Refitは、非常に簡単な方法でRESTサービスを消費するライブラリであり、1つのインターフェイスのみを宣言し、IPUBチームによって作成されます。
文字列操作、URLエンコード、JSON操作、クライアント設定に対処する必要はありません。これはすべてこのライブラリによって自動的に行われます。消費されるAPI RESTのヘッダーを宣言するだけです。
このプロジェクトは、.NETの既存のRefitにインスパイアされています。
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インスタンスは、Tnethttpclientを内部的に使用して呼び出しを行うIgithubapiの実装を生成します。
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));REST APIインターフェイスを宣言するには、2つの義務があります。
REST APIインターフェイスのメソッドは、文字列、レコード、クラス、レコードの動的配列、またはクラスの動的配列を返す手順または関数です。メソッド名は関係ありません。宣言するには、メソッドの種類と相対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、削除、配置、パッチ)。
相対URLには、どこにでもマスク{argument_name/property_name}を持つことができ、繰り返して、引数またはプロパティを挿入できる場所をマークすることができます。次のトピックの詳細。
REST APIインターフェイスでは、メソッドの引数名を使用して、相対URLのマスク{Argument_name/Propertial_name}を置き換えます。このステップでは、Delphi言語で一般的に使用される引数名の最初の文字Aがないケースの不感の名前と名前を許可します。したがって、このケースは同じ結果をもたらします。
[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 、or AContentの場合、引数は要求の本体として使用されます。他の名前を宣言し、この引数で属性[本体]を使用することもできます。引数が本体である場合、引数の種類に関係なく、それはひもにキャストされます。レコードまたはクラスの場合、JSONに自動的にシリアル化します。相対URLのマスク{argument_name/propertial_name}は、内部クエリを含むどこにでもあることがあり、繰り返される可能性があることを忘れないでください。さらに、自動的にエンコードされるため、たとえば、あなたの引数はスペースのある文字列になります。
引数のタイプは重要ではありません。自動的に文字列にキャストします。したがって、たとえば、整数パラメーターを使用できます。
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;インターフェイスの前に、属性[baseurl( 'xxx')]を使用して、base URLをオプションで宣言できます。
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;または、RESTサービスが新しいREST APIインターフェイスを生成するときに直接設定することもできます。これは、サービスが考慮する最初のベースURLです。
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );APIインターフェイスとメソッドで必要なヘッダーを宣言できます。すべてのAPI呼び出しで使用される宣言ヘッダーに、インターフェイスの上に宣言するだけです。
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;1つのAPIメソッドで使用される宣言ヘッダーに、メソッドの上に宣言するだけです。
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;注:1つの方法または1つのREST APIインターフェイスで、多くの[ヘッダー]属性を宣言できます。
ただし、動的ヘッダーを宣言するために、つまり引数やプロパティに応じて、ヘッダー値に{argument_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 ;REST APIインターフェイスでは、読み書き用である限り、あらゆる種類のプロパティを宣言することもでき、事前に「Get」と「セット」を追加するだけで、プロパティと同じ名前のGetterとSetterがある限り、宣言することもできます。例:
[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のようなある種の認証機が必要な場合は、ToAuth2AuthenticatorのようなDelphiのネイティブコンポーネントを使用できます。この場合、あなたはあなた自身によってこの認証機を作成して構成し、それをあなたのREST APIインターフェースのプロパティ認証者に設定する必要があります(それは親インターフェイスで宣言されています、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属性を正常に使用できます。ユニットSystem.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 ;特別なタイプがある場合、カスタムコンバートが必要な場合は、System.json.SerializersでTJSONConverterの独自のJSONコンバーターの子孫を作成し、米国のライブラリに登録できます。
GRestService.RegisterConverters([TNullableStringConverter]);できれば初期化時に、1回だけ登録します。
このライブラリは、インターネット上にいくつかの異なる実装があるため、ヌル可能なタイプを実装していません。いくつかのライブラリには、独自のめまい可能なタイプが既にあります。ただし、カスタムJSONコンバーターを登録する可能性があるため、このライブラリと一緒に使用するためにコードにあらゆる種類のNullableを簡単に実装できます。ここでは、JSONコンバーターを使用した1つの無視可能なタイプの1つの例:
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 特にプログラムを閉じる前に、行われているリクエストをキャンセルできることが重要です。これは、総実行を待つことが数秒かかるため、プログラムの終了を遅らせることができるからです。そのために、APIベースクラス(IIPRESTAPI)でCancelRequestメソッドを作成し、次に電話してください。
LGithubApi.CancelRequest;しかし、これにより、進行中のリクエストの例外が発生します(eiprestserviceCanceled)。次のトピックの詳細。
サービスを使用するときに考慮する必要がある5つの例外があります
例外を「沈黙させる」ために、次のトピックを参照してください。
REST APIインターフェイスで「Try Functions」を宣言できるいくつかの例外を「沈黙させる」ために。非常にシンプルで、「try」名から始まり、ブール波を返す関数を宣言するだけです。メソッドに結果がある場合は、「out」フラグを使用してパラメーターで宣言することもできます。 「試行機能」の有無にかかわらず宣言された同じ2つの方法を参照してください。
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、およびeiprestServiceStatatuscodeの例外を「沈黙させる」ことになります。
GrestServiceとそれによって作成されたREST APIインターフェイスは、スレッドセーフです。
接続は同期しているため、理想はバックグラウンドのAPI関数を呼び出すことです。複数のスレッドがある場合、同じAPIに対して複数のREST APIインターフェイスを作成することもできます。それぞれが異なる接続があります。
このライブラリは完全なクロスプラットフォームであり、Delphi Sydney 10.4で作成およびテストされましたが、おそらくDelphi 10.2 Tokyo以前のバージョンでは機能する可能性があります。
IPUB RefitはMITの下でライセンスされ、ライセンスファイルはこのフォルダーに含まれています。