用於瀏覽器和 Node.js 的基於 Axios 的聲明式 HTTP 用戶端。
由一位習慣了聲明式 HTTP 客戶端的 java 程式設計師編寫。
目標是在所有 javascript/typescript 環境中提供一個簡單、簡潔的 http 用戶端(實際上是 es6 環境) ( 還沒有! )
由於babel-plugin-transform-decorators-legacy
尚不支援參數裝飾器,因此 RetrofitJs 目前僅適用於 TypeScript 環境。
在此引用該提案:
您可以裝飾整個類,以及字段、getter、setter 和方法的聲明。參數和函數聲明不能被修飾。 -- 來自提案裝飾者
也有人提出了這個要求 --> 參數裝飾器?
現在,讓我們專注於聲明自己的http介面而不是http細節,就像Retrofit一樣,少寫,多做。
最後,非常感謝所有編寫或更新 Axios(javascript http 用戶端)和 Retrofit(java http 用戶端)的人們,這些都是很棒的項目,它教會了我很多東西。
該程式無法在 IE8/9/10/11 和其他原生不支援 es6 的環境(通常是瀏覽器)上運行,因為它依賴Proxy
物件( es6 )和Decorator
功能(階段 2 )。
換句話說,RetrofitJs 可以在任何原生支援 es6 的環境下工作。
關於Proxy
對象,我找不到任何支援的polyFill。但對於Decorator
,你可以透過 babel 支援的東西來使用它,例如babel-plugin-transform-decorators-legacy
。
使用 npm:
npm install retrofitjs
// This is typescript demo, also javascript demo( it is if you remove all type define )
// In the first step, you must create your retrofit object.
let client = Retrofit . getBuilder ( )
. setConfig < RetrofitConfig > ( { /** config, you can use retrofit config or axios config */ } )
. addInterceptor ( /** your interceptor */ )
. setErrorHandler ( /** define your error handler */ )
. build ( ) ;
// This is the part of define any interface what you need.
@ HTTP ( "/testing" )
@ Headers ( [ "Cache-Control: no-store" ] )
class TestingClient {
@ GET ( "/demo1/:callByWho/:when" )
public demo1 ( @ Path ( "callByWho" ) name : string , @ Path ( "when" ) time : number ) : RetrofitPromise < string > & void {
}
@ POST ( "/demo2/:file" )
public demo2 ( @ Path ( "file" ) file : string , @ Header ( "cookie" ) val : string , @ Config localConfig : AxiosConfig ) : RetrofitPromise < string > & void {
}
}
// The final step, create your client.
export let testingClient = client . create ( TestingClient ) ;
// When you are calling this method, it is a http call actually.
testingClient . demo1 ( "itfinally" , Date . now ( ) ) . then ( response => {
// any code
} ) . catch ( reason => {
// any code
} ) ;
// And you can also get axios instance.
let axios : AxiosInstance = client . getEngine ( ) ;
和Retrofit一樣,RetrofitJs也是透過攔截器鏈實現的。所有攔截器都依照order
欄位排序,從大到小排序。
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
注意, RealCall
攔截器是最後一個攔截器,而且必須是。
它是一個預設攔截器, order
欄位的值為零。 RetrofitJs 使用它透過 Axios 物件發送所有 http 請求。
為了避免衝突,小於256(不包括256)的訂單欄位編號被保留。
您可以輕鬆地實現自己的攔截器,就像這樣。
class MyInterceptor implement Interceptor {
public order : number = 256 ;
public init ( config : RetrofitConfig ) : void {
// Initializing your interceptor
}
public intercept ( chain : Chain ) : Promise < ResponseInterface < any > > {
// Your code
return chain . proceed ( chain . request ( ) ) ;
}
}
注意chain.proceed( chain.request() )
,此程式碼決定請求是否繼續。
如果使用chain.proceed
調用,當前請求將轉移到下一個攔截器。否則其餘的攔截器將不會被啟動。如果攔截器鏈拋出任何錯誤,整個過程將被終止並啟動錯誤處理程序。
其實插件就是攔截器,你可以為RetrofitJs編寫自己的插件。
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
當你建立一個實例時,它會將所有攔截器加入到Retrofit
。
插件的順序欄位沒有限制。事實上,這些數字是為插件和預設攔截器保留的。
這是所有預設攔截器:
攔截器 | 命令 |
---|---|
真實通話 | 0 |
重試請求攔截器 | 1 |
記錄器攔截器 | 5 |
此攔截器預設為關閉,當冪等請求出現網路異常時,攔截器會隨機重試。就像發送“GET”請求時的連線逾時一樣。
如果你想使用它,你應該在RetrofitConfig
中設定maxTry
和timeout
。
您可以在RetrofitConfig
中設定:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
是一個接口,如果RetryCondition.handler
返回true,攔截器將嘗試再次發送請求。
此攔截器預設為停用,它將記錄所有請求/回應(或錯誤)並將其列印在控制台上。
您可以在RetrofitConfig
中設定:
{
"debug": "boolean"
}
您可以輕鬆取消請求。
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
注意, cancel
api是RetrofitPromise
物件的一個方法,你不能用其他promise物件調用,這是一個例子:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
當您取消要求時,將會拋出RequestCancelException
,您可以在錯誤處理程序中處理或忽略。
如果你想統一處理所有異常,只需實現ErrorHandler
即可。
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
是 axios 給出的參數, exception
是Exception
的實例,可以方便地統一處理所有異常。
這都是例外:
例外 | 描述 |
---|---|
請求取消異常 | 用戶取消 |
連線例外 | ECONNREFUSED 訊號激活 |
套接字異常 | ECONNRESET 訊號激活 |
請求逾時異常 | ECONNABORTED 或 ETIMEDOUT 訊號已激活 |
IO異常 | 其餘情況未知 |
雖然錯誤處理程序可以捕獲所有異常,但這並不意味著Promise.catch
不會被啟動。事實上,有必要在拋出異常時終止正常進程。
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// Your code
}
}
testingClient . demo1 ( ... args ) . then ( response => {
// Normal business process
} ) . catch ( reason => {
// Active after error handler call
} ) ;
在單一方法標誌中,不能用多個裝飾器來裝飾單一參數。
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
如果你想建立像https://127.0.0.1/demo?key1=val1&key2=val2
這樣的 url 查詢,只需如下操作:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
聲明一個查詢鍵值條目。@QueryMap
宣告一個查詢多鍵值條目。輕鬆提交表格。
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
@Field
和@FieldMap
僅在@FormUrlEncoded
聲明方法時有效。
@FormUrlEncoded
聲明這是一個表單。@Field
聲明一個表單鍵值項目。@FieldMap
聲明一個表單多鍵值項目。如果要使用json body請求,可以使用@Body
來修飾參數。
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
不能與@FormUrlEncoded
或@MultiPart
一起使用,因為單一請求中有一個主體。另外, @Body
不能在同一方法中使用多個符號。
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
與上面的情況一樣,參數myBody
將被忽略。
如果要覆寫配置,請使用@Config
來裝飾參數。
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
它將被參數配置包含的欄位覆蓋裝飾器設定(但不包括全域配置)。
動態配置只在請求時有效,對攔截器配置無效,因為攔截器是在呼叫Retrofit.getBuilder().build()
時初始化的,並且只初始化一次。
您可以使用RetrofitJs輕鬆上傳文件,如下所示:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
這是瀏覽器方式:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
這是節點方式:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
與表單提交一樣, @Part
和@PartMap
也僅在@MultiPart
聲明方法時有效。
@MultiPart
聲明這是一個表單。@Part
聲明表單鍵值條目。@PartMap
聲明一個表單多鍵值條目。此外,您還必須小心伺服器中的最大檔案大小設定。如果檔案大小大於伺服器限制,它總是上傳失敗。
最後,不要使用Buffer
物件作為參數,我嘗試使用Buffer物件上傳,但全部失敗,因為Buffer物件只有數據,沒有任何描述,例如檔案名稱、檔案類型。
在瀏覽器中,無法透過Ajax下載文件,因為Ajax總是以字串資料回應,但您可以使用標籤iframe來主動瀏覽器下載。
您可以在節點上下載文件,如下所示:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
將告訴 RetrofitJs 應回傳什麼類型。
這是所有支援的回應類型:
類型 | 價值 |
---|---|
目的 | ResponseType.JSON(預設) |
細繩 | 回應類型.DOCUMENT、回應類型.TEXT |
溪流 | 回應類型.STREAM |
緩衝 | 回應類型.ARRAY_BUFFER |
這是最後一章了,可以看到,RetrofitJs只提供了一個平台,所有的http介面都必須自己寫。
您也可以編寫一個通用介面並對其進行擴展,然後資訊收集器的工作方式如下:
簡而言之,資訊優先級鏈如下:@Config > 方法 > 本類 > 超類
麻省理工學院