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下获得许可,并且该文件夹中包含许可证文件。