happypass
happy pass,pass happy to everybody!
happypass
是一個高度自由化、可定製的 http 請求庫,如果你喜歡掌控自己的代碼,那麼一定會愛上它!
本項目是開源項目,如果大家有好的想法和意見,可以告知我或者一起參與其中,共同維護我們的開源環境。
快速集成
當前最新版本爲: 1.0.7
在 “pubspec.yaml” 文件中加入
dependencies:
happypass: ^1.0.7
https://github.com/CimZzz/happypass
最詳細的示例
happypass
字面意思就是想要 pass happy to everybody
,對於提高工程師使用體驗更是視爲重中之重。所以在 happypass
中,有着大量詳細示例以供參考,幫助我們工程師
能夠快速上手使用。
構建一個請求 (Request)
happypass
將請求對象抽象爲 Request
類,藉由配置 Request
來實現自定義請求的目的。
下面是一個極簡的示例:
import 'package:happypass/happypass.dart';
void main() async {
PassResultResponse result = await Request.quickGet(url: "https://www.baidu.com/", configCallback: (request) {
request.stringChannel();
});
print(result);
}
僅僅幾行代碼,你就完成了一次 GET
請求!
當然,這是最基本的一小部分功能,還有非常多的強大功能幫助你實現主宰自己的 http
請求。
如果想要全面瞭解 happypass
功能覆蓋,還請查看詳細示例
對於上面的示例,我們可以做一些擴展配置,如設置請求頭部等:
request.setRequestHeader("content-type", "application/json");
需要注意的是,Request 無法直接實例化。如果想要構建一個全新的
Request
對象,請使用Request.construct()
方法
想了解 happypass
當前版本全部的配置,點擊查看
RequestPrototype - 請求原型
RequestPrototype(請求原型),也可以理解爲請求的模板
。利用請求原型預先配置好某些屬性,然後在使用的時候快速生成一個配置好的請求,這樣做的好處是避免重複配置請求參數,防止不必要的代碼冗餘。
實例化一個請求原型
import 'package:happypass/happypass.dart';
void main() async {
RequestPrototype prototype = RequestPrototype();
}
像請求一樣,我們可以爲其配置一個基於 utf8 字符串的編解碼器
prototype.stringChannel();
量化生成並執行請求
// 快速孵化請求,量化執行
for(int i = 0 ; i < 10 ; i ++) {
print(await prototype.spawn().GET().doRequest());
}
從上面小例子可以大致地瞭解請求原型的作用 ———— 模板
。
瞭解更多 RequestPrototype
配置,點擊查看 點擊查看
快速請求方法
可能在某些情況下,我們想要簡化代碼複雜度,優化可讀性,可以使用 happypass
提供的快速請求方法:
- quickGet: 快速
GET
請求 - quickPost: 快速
POST
請求
這兩個方法都可以設置請求原型的方式來孵化請求
Request.quickGet(
url: "xxx",
prototype: prototype,
);
Request.quickPost(
url: "xxx",
body: xxx,
prototype: prototype,
);
關於這兩個方法更爲細緻地介紹,請參考樣例,點擊查看
HTTP 攔截器
happypass
提供了強大的攔截器功能,在這裏你可以對自己的請求進行高度自由化的定製!首先讓我們瞭解一下攔截器
的工作原理吧!
攔截器是整個 happypass
的核心,每個請求缺省都會帶有一個 BusinessPassInterceptor
攔截器。該攔截器的作用就是執行實際的請求邏輯。
攔截器的工作原理可以簡單描述爲一條請求鏈路,最終的目的是獲取響應結果。
這裏指的響應結果爲
ResultPassResponse
的子類。該類是happypass
定義的響應結果類
該類有兩個子類,分別表示請求的成功與失敗:
- ErrorPassResponse: 表示請求失敗
- SuccessPassResponse: 表示請求成功
正常情況下,攔截器的工作應該如下
pass request : E -> D -> C -> B -> A -> BusinessPassInterceptor
return response : BusinessPassInterceptor -> A -> B -> C -> D -> E
上述完成了一次攔截工作,Request
的處理和 Response
的構建都在 BusinessPassInterceptor
這個攔截器中完成
如果在特殊情況下,某個攔截器(假設 B)意圖自己完成請求處理,那麼整個流程如下:
pass request : E -> D -> C -> B
return response : B -> C -> D -> E
上述在 B 的位置直接攔截,請求並未傳遞到 BusinessPassInterceptor
,所以 Request 的處理和 Response
的構建都應由 B 完成
需要注意的是,如果攔截器只是對 Request
進行修改或者觀察,並不想實際處理的話,請調用
PassInterceptorChain.waitResponse
方法,表示將 Request
向下傳遞,然後將其結果返回表示將 Response
向上返回。
需要注意的是,如果攔截器不想對請求進行攔截,請務必調用
PassInterceptorChain.waitResponse
方法並且其結果返回(或者根據其結果進行二次加工後的結果)
我們也可以添加攔截器在請求鏈路上做一些自定義行爲,比如攔截來自某個域名的請求
// 爲了方便演示,我們採用 [SimplePassInterceptor] 類,只需傳遞迴調閉包即可實現攔截的功能
final interceptor = SimplePassInterceptor((chain) async {
final httpUrl = HttpUtils.resolveUrl(chain.modifier.getUrl());
if(httpUrl != null && httpUrl.host == "www.baidu.com") {
return ErrorPassResponse(msg: "block www.baidu.com request");
}
return chain.waitResponse();
});
上面例子完成了 block
全部來自 www.baidu.com
域名下的請求。當然,不要忘了將它添加到你的請求配置中,否則這一切都白做了!
Request.get(url: "https://www.baidu.com", configCallback: (request) {
request.addFirstInterceptor(interceptor);
})
該請求最終的響應結果打印出來爲:
block www.baidu.com request
相信這個小例子已經讓大家對攔截器有了一個初步的瞭解
更多攔截器的使用方法與詳細探究,請參考實例,點擊查看
請求 Body、編碼器、解碼器
happypass
中,一次完整請求數據流程大致如下:
- 請求 Body 經過編碼器編碼爲
List<int>
的byte
數據(GET
請求會跳過該步驟) - 將
byte
數據發送,獲得響應的byte
數據 - 將響應的
byte
經過解碼器解碼爲指定的數據結構返回
想必編碼器與解碼器並不陌生,happypass
也提供了一些默認的編解碼器。
happypass
提供的編碼器有:
- GZip2ByteEncoder: GZIP 編碼器。轉換模式爲: List -> List(byte 轉 byte)
- Utf8String2ByteEncoder: utf8 字符串編碼器。轉換模式爲: String -> List(字符串轉
utf8
格式的 byte 數據) - JSON2Utf8StringEncoder: JSON 編碼器。轉換模式爲: Map -> String(Map 轉字符串)
happypass
提供的解碼器有:
- Byte2GZipDecoder: GZIP 解碼器。轉換模式爲: List -> List(byte 轉 byte)
- Byte2Utf8StringDecoder: utf8 字符串解碼器。轉換模式爲: List -> String(
utf8
格式的 byte 數據轉字符串) - Utf8String2JSONDecoder: JSON 解碼器。轉換模式爲: String -> Map(字符串轉 Map)
如果以上編碼器或者解碼器不能滿足你的需要,可以定義一個繼承自 HttpMessageEncoder
或 HttpMessageDecoder
繼承實現自定義的編解碼器。
在需要發送流數據的請求中(比如 POST
請求),必須傳遞一個 body
作爲請求 body:
body 參數有兩種選擇:
- 某種類型數據。該類型數據會經過編碼器層層編碼,最終轉換爲
List<int>
類型的 byte 數據(如果通過編碼器轉換的最終數據不爲List<int>
,則會拋出異常中斷請求)RequestBody
子類
RequestBody
子類會按照一定的規則提供請求數據,具體可以參考相關示例。
下面列舉一下 happypass
提供的 RequestBody
- FormDataBody: 表單鍵值對請求數據,如 “key1=value1&key2=value2” 這種標準表單結構
- MultipartDataBody: Multipart 表單請求數據,可以傳遞文件與流數據
- StreamDataBody: 流數據請求數據,直接讀取流中數據作爲請求數據
如果以上請求體數據不能滿足你的需求,那麼去定義一個繼承自 RequestBody
的類作爲屬於你自己的自定義 RequestBody
吧!
請求中斷
happypass
允許開發者隨時隨地中斷已經發生的或者尚未發生的請求。
使用方法也很簡單:
void main() async {
// 首先我們需要實例化一個 RequestCloser 對象
final requestCloser = RequestCloser();
// 發送 GET 請求,並使用攔截器,在執行完成請求後中斷
final result = await Request.quickGet(url: "https://www.baidu.com", configCallback: (request) {
// 配置請求中斷器
request.addRequestCloser(requestCloser);
});
}
按照上面方法你就成功配置了一個請求中斷器。中斷方法也很簡單
requestCloser.close();
這樣即可中斷請求,無論當前請求處於何種狀態都可以調用此方法(正在執行或者尚未執行,甚至還沒有配置該中斷器之前)
通常來說,中斷都會返回一個 ErrorPassResponse
,但是在特定情況下,也可以中斷請求立即返回一個指定的響應結果:
requestCloser.close(finishResponse: /* a response derived from `ResultPassResponse`*/);
這樣就能由你指定一個任意的請求響應結果,哪怕與該請求期望的響應結果完全無關。
或者在極端情況下,當一箇中斷器應用到多個請求,在中斷的時候,需要根據每個請求返回各自不同的響應結果,可以在構建中斷器的時候那麼做:
RequestCloser(responseChooseCallback: (ChainRequestModifier modifier) {
return ErrorPassResponse();
});
配置一個 RequestCloserResponseChooseCallback
,在中斷請求時,每個請求都會觸發該回調返回對應的響應結果。
結合請求 id
使用可以達到最大效果。
如果該回調返回
null
,那麼依舊會採用close
中指定的響應結果
靈活地運用請求中斷器,可以使工程師們的編碼效率事半功倍。
瞭解更多請求中斷器運用的方法,請參考示例,點擊查看
請求代理
happypass
提供了十分便捷的代理設置方式,使用以下方法即可快速設置代理:
request.addHttpProxy("localhost", 8888);
請求將會嘗試使用 localhost:8888
進行代理,如果代理無法生效,仍然會從本地發起請求。
超時設置
一般我們對於請求時長都有嚴格要求,如果超時需要中斷當前請求連接並返回異常。按照下面方法,可以非常簡單的配置超時時間
// 設置總超時時間
// 總時長超過超時時間將會拋出異常
// * 攔截器處理時間也算在總時長之內
request.setTotalTimeOut(const Duration(seconds: 5));
// 設置連接超時時間
// 連接時長超過超時時間將會拋出異常
request.setConnectTimeOut(const Duration(seconds: 5));
// 設置讀取超時時間
// 讀取時長超過超時時間將會拋出異常
request.setReadTimeOut(const Duration(seconds: 5));
happypass
也提供了相當詳細的示例文檔,點擊查看
請求運行代理
這個概念容易和 http
代理混淆,兩者的區別如下:
請求運行代理: 在請求配置與解析時,可能會發生一些比較耗時的操作(例如解析 JSON),官方建議使用
Isolate
來單獨處理這些操作,以防止造成
卡頓等情況,而請求運行代理就是爲了解決這個問題而生的。你可以在請求代理中使用一個Isolate
去執行參數中的回調,然後將其結果返回。請求 HTTP 代理: 使用指定的 HTTP 代理服務器發送請求。
在 Flutter
中,compute
方法與請求運行代理高度契合,可以很方便配置
setRequestRunProxy(<T, Q>(asyncCallback, message) async {
return await compute(asyncCallback, message);
});
這樣,複雜的編解碼操作都交給另外的 Isolate
處理了。
示例中也介紹了 Dart
的實現方式,點擊查看
設置 Cookie 管理器
happypass
可以配置 CookieManager
,幫助開發者管理請求中的 Cookie
。
request.setCookieManager(MemoryCacheCookieManager());
MemoryCacheCookieManager
是happypass
提供的一個內存 Cookie 緩存管理器,開發者可以自定義CookieManager
將之替換。
- 默認情況下,請求是不會攜帶
CookieManger
聯繫我
如果您在使用的過程中有更好的想法,或者發現什麼問題,都可以聯繫我,共同進行交流:
QQ: 1152564696
Mail: [email protected]