flutter dio網路請求封裝實現
flutter dio網路請求封裝實現
文章友情連結: https://juejin.im/post/6844904098643312648
在Flutter專案中使用網路請求的方式大致可分為兩種,分別是Dart原生的網路請求 HttpClient類以及第三方開源的網路請求庫。在Dart社群開源的第三方http請求庫中Flutter中文網開源的Dio庫人氣最高。下面我們先來比較下這兩種網路請求方式,然後再看怎麼基於 Dio庫封裝方便使用的網路請求工具類HttpManager。
網路請求庫比較
HttClient類
Dart IO庫中提供了用於發起Http請求的一些類,我們可以直接使用HttpClient來發起請求。
使用HttpClient發起請求共分為五步:
- 建立一個HttpClient
HttpClient httpClient = new HttpClient();
複製程式碼
- 開啟Http連線,設定請求頭
HttpClientRequest request = await httpClient.getUrl(uri);
複製程式碼
這一步可以使用任意Http Method,如httpClient.post(...)、httpClient.delete(...)等。如果包含Query引數,可以在構建uri時新增,如:
Uri uri=Uri(scheme: "https", host: "flutterchina.club", queryParameters: { "xx":"xx", "yy":"dd" }); 複製程式碼
通過HttpClientRequest可以設定請求header,如:
request.headers.add("user-agent", "test");
複製程式碼
如果是post或put等可以攜帶請求體方法,可以通過HttpClientRequest物件傳送request body,如:
String payload="...";
request.add(utf8.encode(payload));
//request.addStream(_inputStream); //可以直接新增輸入流
複製程式碼
- 等待連線伺服器
HttpClientResponse response = await request.close(); 複製程式碼
這一步完成後,請求資訊就已經發送給伺服器了,返回一個HttpClientResponse物件,它包含響應頭(header)和響應流(響應體的Stream),接下來就可以通過讀取響應流來獲取響應內容。
- 讀取響應內容
String responseBody = await response.transform(utf8.decoder).join();
複製程式碼
我們通過讀取響應流來獲取伺服器返回的資料,在讀取時我們可以設定編碼格式,這裡是utf8。
- 請求結束,關閉HttpClient
httpClient.close();
複製程式碼
關閉client後,通過該client發起的所有請求都會中止。
以上的步驟是dart原生網路HttpClient使用方式,可以發現直接使用HttpClient發起網路請求是比較麻煩的,很多事情都得手動處理,如果再涉及到檔案上傳/下載、Cookie管理等就會變得非常繁瑣,並且HttpClient本身功能較弱,很多常用功能都不支援。
Dio庫
dio是一個強大的Dart Http請求庫,支援Restful API、FormData、攔截器、請求取消、Cookie管理、檔案上傳/下載、超時等...
- pubspec.yaml 新增依賴
dependencies: dio: ^x.x.x #請使用pub上的最新版本
- 匯入引用並建立dio 例項
import 'package:dio/dio.dart';
Dio dio = Dio();
接下來就可以通過 dio例項來發起網路請求了,注意,一個dio例項可以發起多個http請求,一般來說,APP只有一個http資料來源時,dio應該使用單例模式。
- 發起網路請求
Get 請求
Response response;
response=await dio.get("/test?id=12&name=cheney")
print(response.data.toString());
Post請求
Response response;
response=await dio.post("/test",data:{"id":12,"name":"cheney"})
print(response.data.toString());
以上就是Dio庫網路請求的基本使用,是不是很簡單,除了這些基本的用法,dio還支援請求配置、攔截器等,官方資料比較詳細,故在這裡不再贅述,詳情可以參考dio主頁:github.com/flutterchin… 。
封裝Dio工具類
為什麼要封裝 dio
做一些公共處理,方便靈活的使用。
做那些封裝
- 統一處理請求字首;(www.xx.com/api/v1 不用每個請求都加字首)
- 統一輸出請求或響應資訊;
- 統一錯誤資訊處理;
- 相容多種網路請求、支援檔案上傳、下載;
- 支援同步回撥與非同步Future 兩種形式
- 返回資料自動轉json格式並預設解析公共資料模型;
目錄
類名 | 描述 |
---|---|
HttpManager | 網路請求管理類 |
HttpError | 網路請求統一錯誤類 |
LogInterceptor | 網路請求工具類 |
HttpManager
import 'dart:core'; import 'package:connectivity/connectivity.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_common_utils/http/http_error.dart'; import 'package:flutter_common_utils/log_util.dart'; ///http請求成功回撥 typedef HttpSuccessCallback<T> = void Function(dynamic data); ///失敗回撥 typedef HttpFailureCallback = void Function(HttpError data); ///資料解析回撥 typedef T JsonParse<T>(dynamic data); /// @desc 封裝 http 請求 /// @time 2019/3/15 10:35 AM /// @author Cheney class HttpManager { init : 初始化baseUrl,超時時間等 get : get請求同步回撥 post : post請求同步回撥 upload : 檔案上傳同步回撥 download : 檔案下載同步回撥 getAsync : get 請求非同步方式 postAsync : post 請求非同步方式 uploadAsync : 檔案上傳非同步方式 downloadAsync : 檔案下載非同步方式 [...] }
詳細原始碼見最後。
這裡處理了網路連線判斷、取消網路請求、預設的資料格式解析等。 預設解析的資料格式:
{ "data":{}, "statusCode":"0", "statusDesc":"02032008:使用者授信未通過", "timestamp":1569206576392 }
大家可根據自己的需求更改成自己的資料格式處理
HttpError
import 'package:dio/dio.dart'; /// @desc 網路請求錯誤 /// @time 2019/3/20 10:02 AM /// @author Cheney class HttpError { ///HTTP 狀態碼 static const int UNAUTHORIZED = 401; static const int FORBIDDEN = 403; static const int NOT_FOUND = 404; static const int REQUEST_TIMEOUT = 408; static const int INTERNAL_SERVER_ERROR = 500; static const int BAD_GATEWAY = 502; static const int SERVICE_UNAVAILABLE = 503; static const int GATEWAY_TIMEOUT = 504; ///未知錯誤 static const String UNKNOWN = "UNKNOWN"; ///解析錯誤 static const String PARSE_ERROR = "PARSE_ERROR"; ///網路錯誤 static const String NETWORK_ERROR = "NETWORK_ERROR"; ///協議錯誤 static const String HTTP_ERROR = "HTTP_ERROR"; ///證書錯誤 static const String SSL_ERROR = "SSL_ERROR"; ///連線超時 static const String CONNECT_TIMEOUT = "CONNECT_TIMEOUT"; ///響應超時 static const String RECEIVE_TIMEOUT = "RECEIVE_TIMEOUT"; ///傳送超時 static const String SEND_TIMEOUT = "SEND_TIMEOUT"; ///網路請求取消 static const String CANCEL = "CANCEL"; String code; String message; HttpError(this.code, this.message); HttpError.dioError(DioError error) { message = error.message; switch (error.type) { case DioErrorType.CONNECT_TIMEOUT: code = CONNECT_TIMEOUT; message = "網路連線超時,請檢查網路設定"; break; case DioErrorType.RECEIVE_TIMEOUT: code = RECEIVE_TIMEOUT; message = "伺服器異常,請稍後重試!"; break; case DioErrorType.SEND_TIMEOUT: code = SEND_TIMEOUT; message = "網路連線超時,請檢查網路設定"; break; case DioErrorType.RESPONSE: code = HTTP_ERROR; message = "伺服器異常,請稍後重試!"; break; case DioErrorType.CANCEL: code = CANCEL; message = "請求已被取消,請重新請求"; break; case DioErrorType.DEFAULT: code = UNKNOWN; message = "網路異常,請稍後重試!"; break; } } @override String toString() { return 'HttpError{code: $code, message: $message}'; } }
這裡設定了多種錯誤的描述,大家可根據需求修改。
LogInterceptor
import 'package:dio/dio.dart'; import 'package:flutter_common_utils/log_util.dart'; void log2Console(Object object) { LogUtil.v(object); } /// @desc 自定義日誌攔截器 ///@time 2019/3/18 9:15 AM /// @author Cheney class LogInterceptor extends Interceptor { LogInterceptor({ this.request = true, this.requestHeader = true, this.requestBody = false, this.responseHeader = true, this.responseBody = false, this.error = true, this.logPrint = log2Console, }); [...] }
詳細原始碼見最後。
這裡預設使用 LogUtl 輸出日誌,大家可根據需要換成自己的日誌輸出工具類
Step1: 初始化
//初始化 Http, HttpManager().init( baseUrl: Api.getBaseUrl(), interceptors: [ HeaderInterceptor(), LogInterceptor(), ], );
Step2:建立網路請求
///同步回撥模式 ///get 網路請求 void _get(){ HttpManager().get( url: "/app/info", params: {"iouCode": iouCode}, successCallback: (data) { }, errorCallback: (HttpError error) { }, tag: "tag", ); } ///post 網路請求 void _post(){ HttpManager().post( url: "/app/info", data: {"iouCode": iouCode}, successCallback: (data) { }, errorCallback: (HttpError error) { }, tag: "tag", ); } ///下載檔案 void _download(){ HttpManager().download( url: "/app/download", savePath: "/savePath", onReceiveProgress: (int count, int total) { }, successCallback: (data) { }, errorCallback: (HttpError error) { }, tag: tag, ); } ///上傳檔案 void _upload() async{ FormData data = FormData.fromMap({ "file": await MultipartFile.fromFile(path, filename: "$photoTime"), }); HttpManager().upload( url: "/app/upload", data: data, tag: "tag", successCallback: (data) { }, errorCallback: (HttpError error) { }, ); } ///非同步模式 ///get 請求 void _getAysnc() async{ String timestamp = await HttpManager().getAsync(url: "/app/info", tag: "syncTime"); } ///post 請求 void _postAysnc() async{ await HttpManager().postAsync( url: "app/info", tag: "tag", data: { 'bannerTypes': ["wealthBanner"], }, jsonParse: (json) => Pager(json, (data) => ImageAd(data))) } ///下載檔案 void _downloadAsync() async{ await HttpManager().downloadAsync( url: "/app/download", savePath: "/savePath", onReceiveProgress: (int count, int total) { }, tag: "tag", ); } ///上傳檔案 void _uploadAsync() async{ FormData data = FormData.fromMap({ "file": await MultipartFile.fromFile(path, filename: "$photoTime"), }); await HttpManager().uploadAsync( url: "/app/upload", data: data, tag: "tag", ); }
最後
如果在使用過程遇到問題,歡迎下方留言交流。
工具類庫地址
文章友情連結: https://juejin.im/post/6844904098643312648