Refit IPUB هي مكتبة تستهلك خدمات REST بطريقة بسيطة للغاية ، مع إعلان واجهة واحدة فقط ويتم إنشاؤها بواسطة فريق IPUB.
ليس عليك التعامل مع معالجة السلسلة ، وترميز URL ، ومعالجة JSON ، وإعدادات العميل. يتم كل هذا تلقائيًا بواسطة هذه المكتبة ، تحتاج فقط إلى إعلان رأس راحة API التي سيتم استهلاكها.
هذا المشروع مستوحى / بناءً على إعادة التجديد الموجودة في .NET ، ويحول واجهة برمجة تطبيقات REST إلى واجهة حية:
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 ، هناك التزامان:
يمكن أن تكون طرق واجهة API REST إجراءً أو وظيفة تُرجع سلسلة أو سجل أو فئة أو صفيف ديناميكي من السجل أو مجموعة ديناميكية من الفصل. اسم الطريقة لا يهم. للإعلان ، يجب أن تعلن سمة تُعلم عن url طريقة وعنوان 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 ، Delete ، Put and Patch).
يمكن أن يحتوي عنوان URL النسبي على أقنعة {argument_name/property_name} ، في أي مكان ويمكن أن تكرر ، لتمييز مكان إدراج وسيطة أو خاصية. مزيد من التفاصيل في الموضوع التالي.
في واجهة API REST الخاصة بك ، سيتم استخدام اسم الوسائط للطرق لاستبدال الأقنعة {engument_name/property_name} في عنوان URL النسبي. في هذه الخطوة ، نسمح للأسماء والأسماء غير الحساسة للحالة بدون الحرف الأول من أسماء الوسيطة المستخدمة بشكل شائع في لغة دلفي. لذلك ، سيكون لهذه الحالات نفس النتيجة:
[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 تلقائيًا. تذكر أن القناع {engument_name/property_name} في عنوان URL النسبي ، يمكن أن يكون في أي مكان ، بما في ذلك الاستعلامات الداخلية ، ويمكن أن يكرر. بالإضافة إلى ذلك ، سيتم ترميزها تلقائيًا ، لذلك يمكن أن تكون وسيطةك سلسلة ذات مسافات ، على سبيل المثال.
لا يهم نوع الوسيطة ، سنقوم بإلقاء القطع تلقائيًا. حتى تتمكن من استخدام ، على سبيل المثال ، معلمة عدد صحيح:
[Get( ' /users/{AUserId} ' )]
function GetUser ( const AUserId: Integer): string;يمكنك الإعلان اختياريًا عنوان URL الأساسي باستخدام السمة [baseurl ('xxx')] قبل الواجهة:
[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 ;ملاحظة: يمكنك إعلان العديد من السمة [الرؤوس] بطريقة واحدة أو في واجهة API REST واحدة.
ولكن لإعلان الرؤوس الديناميكية ، أي اعتمادًا على الوسائط أو الخصائص ، يمكنك أيضًا استخدام قناع {engument_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 Like Toauth2Authenticator. في هذه الحالة ، ستحتاج إلى إنشاء هذا المصدق وتكوينه من خلال نفسك وتعيينه في مصادقة خاصية لواجهة 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 ;ملاحظة: تحتاج إلى تدمير المصادقة من خلال نفسك بعد استخدامه ، لن تفعل خدمة REST داخليًا.
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 ;إذا كان لديك نوع خاص ، يحتاج إلى تحويل مخصص ، يمكنك إنشاء محول JSON الخاص بك من tjsonconverter في system.json.serializers وتسجيله في المكتبة الأمريكية:
GRestService.RegisterConverters([TNullableStringConverter]);سوف تسجل مرة واحدة فقط ، ويفضل في التهيئة.
لا تنفذ هذه المكتبة أنواعًا قابلة للإلغاء لأن هناك العديد من التطبيقات المختلفة على الإنترنت ، ولديها العديد من المكتبات بالفعل نوعها الخاص. ولكن مع إمكانية تسجيل محولات 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 .الآن ، يمكنك استخدام 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 من المهم أن تكون قادرًا على إلغاء أي طلب يتم تقديمه ، خاصة قبل إغلاق البرنامج ، لأن انتظار التنفيذ الكلي قد يستغرق بضع ثوان ، وبالتالي تأخير إغلاق البرنامج. لذلك ، أنشأنا طريقة CancelRequest في فئة قاعدة API (iiprestapi) ، ثم اتصل فقط:
LGithubApi.CancelRequest;ولكن هذا سوف يثير استثناء للطلب قيد التقدم (EiprestServiceCanceled). مزيد من المعلومات في الموضوعات التالية.
لدينا خمسة استثناءات يجب مراعاتها عند استخدام الخدمة
إلى "صمت" الاستثناءات ترى الموضوع التالي.
إلى "إسكات" بعض الاستثناءات التي يمكنك إعلانها "تجرب وظائف" في واجهة API REST. بسيطة للغاية ، فقط إعلان الوظائف التي تبدأ باسم "Try" وإعادة Boolean. إذا كانت الطريقة لها نتيجة ، فيمكنك أيضًا إعلانها في المعلمة باستخدام العلم "الخارجي". شاهد نفس الطريقتين المعلنين مع وبدون "Try Function":
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 ;الآن ، سوف "إسكات" "إسكات" استثناءات EiprestServiceCanceled ، eiprestservicefailed ، eiprestservicejson و eiprestservicestatuscode.
واجهات GrestService و REST API التي تم إنشاؤها من خلال الخيط آمن.
نظرًا لأن الاتصالات متزامنة ، فإن المثالية هي استدعاء وظائف API في الخلفية. إذا كان لديك عدة مؤشرات ترابط ، فيمكنك أيضًا إنشاء واجهات API REST متعددة لنفس واجهة برمجة التطبيقات (API) ، فسيكون لكل واحد اتصال مختلف.
هذه المكتبة بالكامل منصة وتم إجراءها واختبارها على Delphi Sydney 10.4 ، ولكن من المحتمل أن تعمل على الإصدارات السابقة ، ربما في Delphi 10.2 Tokyo أو أحدث.
تم ترخيص إعادة تجديد IPUB بموجب معهد ماساتشوستس للتكنولوجيا ، ويتم تضمين ملف الترخيص في هذا المجلد.