iPub Refit เป็นไลบรารีเพื่อใช้บริการ REST ในวิธีที่ง่ายมากประกาศเพียงหนึ่งอินเทอร์เฟซและสร้างโดยทีมงาน iPub
คุณไม่ต้องจัดการกับการจัดการสตริงการเข้ารหัส URL การจัดการ JSON การตั้งค่าลูกค้า ทั้งหมดนี้ทำโดยอัตโนมัติโดยห้องสมุดนี้คุณเพียงแค่ต้องประกาศส่วนหัวของส่วนที่เหลือ API ที่จะบริโภค
โครงการนี้เป็นแรงบันดาลใจ / ขึ้นอยู่กับการปรับแต่งที่มีอยู่ใน. NET และเปลี่ยนส่วนที่เหลือของคุณให้เป็นอินเทอร์เฟซสด:
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;วิธีการมาตรฐานทั้งหมดได้รับการสนับสนุน (รับโพสต์ลบใส่และแพทช์)
URL ที่สัมพันธ์กันสามารถมีหน้ากาก {argion_name/property_name} ได้ทุกที่และสามารถทำซ้ำเพื่อทำเครื่องหมายว่าสามารถแทรกอาร์กิวเมนต์หรือคุณสมบัติได้ รายละเอียดเพิ่มเติมในหัวข้อถัดไป
ในอินเตอร์เฟส REST API ของคุณชื่ออาร์กิวเมนต์ของวิธีการจะถูกใช้เพื่อแทนที่มาสก์ {argion_name/property_name} ใน URL สัมพัทธ์ ในขั้นตอนนี้เราอนุญาตให้ใช้ชื่อและชื่อที่ไม่รู้สึกตัวโดยไม่มีตัวอักษรตัวแรกของชื่ออาร์กิวเมนต์ที่ใช้กันทั่วไปในภาษา 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 การโต้แย้งจะถูกใช้เป็นร่างกายของคำขอ นอกจากนี้คุณยังสามารถประกาศชื่ออื่นและใช้แอตทริบิวต์ [body] ในการโต้แย้งนี้ เมื่อการโต้แย้งเป็นร่างกายไม่ว่าจะเป็นประเภทการโต้แย้งมันจะถูกคัดเลือกเป็นสตริง หากเป็นบันทึกหรือชั้นเรียนเราจะทำให้เป็นอนุกรมกับ JSON โดยอัตโนมัติ โปรดจำไว้ว่าหน้ากาก {argion_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 ;หมายเหตุ: คุณสามารถประกาศแอตทริบิวต์ [ส่วนหัว] จำนวนมากในวิธีเดียวหรือในอินเตอร์เฟส REST API เดียว
แต่ในการประกาศส่วนหัวแบบไดนามิกนั่นคือขึ้นอยู่กับอาร์กิวเมนต์หรือคุณสมบัติคุณยังสามารถใช้หน้ากาก {argion_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 ;เมื่อคุณต้องการ Authenticator บางชนิดเช่น OAuth1 หรือ OAuth2 คุณสามารถใช้ส่วนประกอบดั้งเดิมของ Delphi เช่น ToAuth2authenticator ในกรณีนี้คุณจะต้องสร้างและกำหนดค่า Authenticator นี้ด้วยตัวคุณเองและตั้งค่าในคุณสมบัติ Authenticator ของอินเตอร์เฟส 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 ;หมายเหตุ: คุณต้องทำลายตัวตรวจสอบด้วยตัวคุณเองหลังจากใช้งานบริการ 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 Base (IIPRestapi) จากนั้นโทร:
LGithubApi.CancelRequest;แต่สิ่งนี้จะเพิ่มข้อยกเว้นสำหรับคำขอที่กำลังดำเนินการ (eiprestserviceCanceled) ข้อมูลเพิ่มเติมในหัวข้อถัดไป
เรามีข้อยกเว้นห้าข้อที่ต้องพิจารณาเมื่อใช้บริการ
ถึง "เงียบ" ข้อยกเว้นดูหัวข้อถัดไป
ในการ "เงียบ" ข้อยกเว้นบางอย่างที่คุณสามารถประกาศ "ลองฟังก์ชั่น" ในส่วนต่อประสาน REST API ของคุณ ง่ายมากเพียงแค่ประกาศฟังก์ชั่นที่เริ่มต้นด้วยชื่อ "ลอง" และส่งคืนบูลีน หากวิธีการมีผลลัพธ์คุณสามารถประกาศในพารามิเตอร์ด้วยธง "ออก" ดูสองวิธีเดียวกันที่ประกาศว่ามีและไม่มี "ลองใช้ฟังก์ชัน":
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 และ eiprestservicestatuscode
ส่วนต่อประสาน GRESTSERVICE และ REST API ที่สร้างขึ้นโดย Thread Safe
เนื่องจากการเชื่อมต่อเป็นแบบซิงโครนัสอุดมคติคือการเรียกฟังก์ชั่น API ในพื้นหลัง หากคุณมีหลายเธรดคุณสามารถสร้างอินเตอร์เฟส REST API หลายตัวสำหรับ API เดียวกันแต่ละตัวจะมีการเชื่อมต่อที่แตกต่างกัน
ห้องสมุดนี้เต็มรูปแบบข้ามแพลตฟอร์มและทำการทดสอบและทดสอบใน Delphi Sydney 10.4 แต่มีแนวโน้มที่จะทำงานกับเวอร์ชั่นก่อนหน้านี้อาจจะอยู่ใน Delphi 10.2 Tokyo หรือใหม่กว่า
iPub Refit ได้รับอนุญาตภายใต้ MIT และไฟล์ใบอนุญาตจะรวมอยู่ในโฟลเดอร์นี้