IPUB REFIT是一個以非常簡單的方式消費REST服務的庫,僅聲明一個界面並由IPUB團隊創建。
您不必處理字符串操作,URL編碼,JSON操作,客戶設置。這一切都是由此庫自動完成的,您只需要聲明將要消耗的API REST的標題即可。
該項目啟發 /基於.NET中現有的重新裝置,並將您的REST API變成實時界面:
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接口,有兩種義務:
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;支持所有標準方法(獲取,發布,刪除,put和補丁)。
相對URL可以在任何地方都有basks {groment_name/property_name},並且可以重複,以標記可以插入參數或屬性的位置。下一個主題中的更多詳細信息。
在您的REST API接口中,方法的參數名稱將用於替換相對URL中的basks {grogng_name/property_name}。在此步驟中,我們允許案例不敏感的名稱和名稱,而沒有第一個字母的參數名稱A的名稱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 ,則該論點將用作請求的主體。您還可以聲明其他名稱並在此參數中使用屬性[正文]。當一個論點是身體時,無論參數類型是什麼,它都會被施放為字符串。如果是記錄或類,我們將自動將其序列化為JSON。請記住,相對URL中的mask {gright_name/property_name}可以在任何地方,包括內部查詢,並且可以重複。另外,它將自動編碼,因此您的參數可以是帶有空格的字符串。
參數的類型無關緊要,我們將自動施放串起。因此,您可以使用一個整數參數:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;您可以選擇在接口之前使用屬性[baseurl('xxx')]聲明基本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 ;對於將在一種API方法中使用的聲明標題,只需在該方法上方聲明:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;注意:您可以在一種方法或一個REST API接口中聲明許多[標題]屬性。
但是要聲明動態標頭,也就是說,根據參數或屬性,您還可以在標題值上使用{grogng_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接口中,您還可以聲明任何類型的屬性,只要它用於閱讀和寫作,並且具有與屬性相同名稱的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的本機組件,例如toauth2authenticator。在這種情況下,您需要通過自我創建和配置此身份驗證器,並將其設置在REST API接口的屬性身份驗證器中(它已在Parent Interface,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屬性。允許使用單位系統的所有屬性。例子:
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中創建自己的JSON轉換器後代,然後在美國庫中註冊它:
GRestService.RegisterConverters([TNullableStringConverter]);您只需註冊一次,最好是初始化。
該庫無法實現無效類型,因為Internet上有幾種不同的實現,幾個庫已經具有自己的無效類型。但是,有可能註冊自定義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 .現在,您可以在此庫中使用無效的方法,例如:
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)提供例外。下一個主題中的更多信息。
我們有五個使用服務時需要考慮的例外
要“沉默”例外,請參閱下一個主題。
為了“沉默”一些例外,您可以在REST API接口中聲明“嘗試”功能。非常簡單,只需聲明以“嘗試”名稱和返回布爾值開始的函數。如果該方法具有結果,則還可以用“ OUT”標誌在參數中聲明它。請參閱有或沒有“嘗試函數”的情況下聲明的兩種方法:
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將“沉默” EiprestServiceCecled,EiprestServiceFail,EiprestServicejson和Eiprestservicestatuscode的異常。
它創建的GrestService和REST API接口是線程安全的。
由於連接是同步的,因此理想是在後台調用API函數。如果您有多個線程,也可以為同一API創建多個REST API接口,每個API都會具有不同的連接。
該庫完全跨平台,並在Delphi Sydney 10.4上進行了測試,但它很可能在以前的版本上使用,可能在Delphi 10.2 Tokyo或更新中使用。
IPUB REFIT在MIT下獲得許可,並且該文件夾中包含許可證文件。