El IPUB RELET es una biblioteca para consumir servicios REST de una manera muy simple, declarando solo una interfaz y es creada por el equipo de IPUB.
No tiene que lidiar con la manipulación de cadenas, la codificación de URL, la manipulación JSON, la configuración del cliente. Todo esto se hace automáticamente por esta biblioteca, solo necesita declarar el encabezado del descanso API que se consumirá.
Este proyecto inspiró / basado en el reiniciado existente en .NET, y convierte su API REST en una interfaz en vivo:
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 ;La instancia de GrestService genera una implementación de Igithubapi que usa internamente tnethttpClient para hacer sus llamadas:
var
LGithubApi: IGithubApi;
LUser: TUser;
begin
LGithubApi := GRestService.& For <IGithubApi>;
LUser := LGithubApi.GetUser( ' viniciusfbb ' );
Showmessage(LGithubApi.GetUserJson( ' viniciusfbb ' ));Para declarar la interfaz API REST, hay dos obligaciones:
Los métodos de la interfaz API REST pueden ser un procedimiento o una función que devuelve una cadena, registro, clase, matriz dinámica de registro o matriz dinámica de clase. El nombre del método no importa. Para declarar que debe declarar un atributo informando el tipo de método y la URL relativa, debe declarar el tipo de método del método de interfaz y la URL relativa
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
[Post( ' /users/{AUser}?message={AMessage} ' )]
function GetUserJson ( const AUser, AMessage: string): string;Todos los métodos estándar son compatibles (obtener, publicar, eliminar, poner y parche).
La URL relativa puede tener máscaras {argumento_name/propiedad_name}, en cualquier lugar y puede repetir, para marcar dónde se puede insertar un argumento o propiedad. Más detalles en el siguiente tema.
En su interfaz API REST, el nombre de los métodos de los argumentos se utilizará para reemplazar las máscaras {argument_name/property_name} en URL relativa. En este paso, permitimos nombres y nombres insensibles al caso sin la primera letra A de nombres de argumentos utilizados comúnmente en el lenguaje Delphi. Entonces, estos casos tendrán el mismo resultado:
[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 el nombre del argumento es Body , ABody , BodyContent , ABodyContent , Content o AContent , el argumento se utilizará como el cuerpo de la solicitud. También puede declarar otro nombre y usar el atributo [cuerpo] en este argumento. Cuando un argumento es un cuerpo, sin importar el tipo de argumento, se lanzará a String. Si es un registro o clase, lo serializaremos a un JSON automáticamente. Recuerde que la máscara {argument_name/property_name} en URL relativa puede estar en cualquier lugar, incluidas las consultas internas, y puede repetirse. Además, se codificará automáticamente, por lo que su argumento puede ser una cadena con espacios, por ejemplo.
El tipo de argumento no importa, lanzaremos a la cadena automáticamente. Para que pueda usar, por ejemplo, un parámetro entero:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;Puede declarar opcionalmente la URL base utilizando el atributo [BaseUrl ('xxx')] antes de la interfaz:
[BaseUrl( ' https://api.github.com ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;O puede establecer directamente cuando el servicio REST genere una nueva interfaz API REST, esta es la primera URL base que el servicio considerará:
LGithubApi := GRestService.& For <IGithubApi>( ' https://api.github.com ' );Puede declarar los encabezados necesarios en la interfaz API y el método. Para declarar encabezados que se utilizarán en todas las llamadas de API, simplemente declare por encima de la interfaz:
[Headers( ' User-Agent ' , ' Awesome Octocat App ' )]
[Headers( ' Header-A ' , ' 1 ' )]
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
end ;Para declarar encabezados que se utilizarán en un método API, simplemente declare por encima del método:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Headers( ' Header-A ' , ' 1 ' )]
[Get( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string): string;
end ;Nota: Puede declarar muchos atributos [encabezados] en un método o en una interfaz API REST.
Pero para declarar encabezados dinámicos, es decir, dependiendo de los argumentos o propiedades, también puede usar la máscara {argument_name/property_name} en el valor del encabezado. Pero también hay otra opción exclusiva para declarar un encabezado usando un argumento directamente. En este último caso, deberá declarar el atributo [encabezado] en la declaración de parámetros:
IGithubApi = interface (IipRestApi)
[ ' {4C3B546F-216D-46D9-8E7D-0009C0771064} ' ]
[Post( ' /users/{AUser} ' )]
function GetUserJson ( const AUser: string; [Header( ' Authorization ' )] const AAuthToken: string): string;
end ;En su interfaz API REST, también puede declarar cualquier tipo de propiedad, siempre que sea para leer y escribir, y tiene Getter y Setter con el mismo nombre que la propiedad simplemente agregando "Get" y "Establecer" de antemano. Ejemplo:
[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 ;Cuando necesite algún tipo de autenticador como OAuth1 o Oauth2, puede usar los componentes nativos de Delphi como ToAuth2Authenticator. En este caso, deberá crear y configurar este autenticador por usted mismo y establecerlo en la propiedad Autenticador de su interfaz REST API (se ha declarado en la interfaz principal, 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 ;Nota: Debe destruir el autenticador por usted mismo después de usarlo, el servicio REST no lo hará internamente.
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 ;Los tipos utilizados en su interfaz pueden usar los atributos JSON normalmente. Se permiten todos los atributos de la unidad System.json.serializadores. Ejemplo:
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 tiene un tipo especial, que necesita un converso personalizado, puede crear su propio convertidor JSON descendiente del TJSONConverter en System.json.serializers y registrarlo en la biblioteca estadounidense:
GRestService.RegisterConverters([TNullableStringConverter]);Se registrará solo una vez, preferiblemente en la inicialización.
Esta biblioteca no implementa tipos anulables porque hay varias implementaciones diferentes en Internet, varias bibliotecas ya tienen su propio tipo anulable. Pero con la posibilidad de registrar convertidores JSON personalizados, es fácil implementar cualquier tipo de anulable en el código para usar junto con esta biblioteca. Aquí un ejemplo de un tipo anulable con el convertidor 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 .Ahora, puede usar el anulable con esta biblioteca como:
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 importante poder cancelar cualquier solicitud que se esté haciendo, especialmente antes de cerrar el programa, porque esperar la ejecución total puede demorar unos segundos y retrasar el cierre del programa. Para eso, creamos el método CancelRequest en la clase base API (iiprestapi), luego solo llame::
LGithubApi.CancelRequest;Pero esto planteará una excepción para la solicitud en progreso (EiprestServiceCanceled). Más información en los próximos temas.
Tenemos cinco excepciones que deben considerarse al usar el servicio
Para "silenciar" las excepciones, vea el siguiente tema.
Para "silenciar" algunas excepciones, puede declarar "pruebe las funciones" en su interfaz API REST. Es muy simple, solo declara funciones que comienzan con el nombre de "prueba" y regresan boolean. Si el método tiene un resultado, también puede declararlo en el parámetro con el indicador "Out". Consulte los mismos dos métodos declarados con y sin "función de prueba":
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 ;Ahora, el TryGetuser "silenciará" las excepciones eiprestservicecanceled, eiprestservicefailed, eiprestservicejson y eiprestservicestatuscode.
El servicio Grestservice y las interfaces API REST creadas por él son seguras.
Como las conexiones son sincrónicas, el ideal es llamar a las funciones API en segundo plano. Si tiene múltiples hilos, también puede crear múltiples interfaces API REST para la misma API, cada una tendrá una conexión diferente.
Esta biblioteca completa completa y se hizo y se probó en Delphi Sydney 10.4, pero es probable que funcione en versiones anteriores, probablemente en Delphi 10.2 Tokio o más nuevo.
El IPUB RELET tiene licencia bajo MIT, y el archivo de licencia se incluye en esta carpeta.