flutter dio網絡請求封裝實現

flutter dio網絡請求封裝實現

文章友情鏈接:   https://juejin.im/post/6844904098643312648

在Flutter項目中使用網絡請求的方式大致可分爲兩種,分別是Dart原生的網絡請求 HttpClient類以及第三方開源的網絡請求庫。在Dart社區開源的第三方http請求庫中Flutter中文網開源的Dio庫人氣最高。
  下面我們先來比較下這兩種網絡請求方式,然後再看怎麼基於 Dio庫封裝方便使用的網絡請求工具類HttpManager。

網絡請求庫比較

HttClient類

Dart IO庫中提供了用於發起Http請求的一些類,我們可以直接使用HttpClient來發起請求。

使用HttpClient發起請求共分爲五步:

  1. 創建一個HttpClient
 HttpClient httpClient = new HttpClient();
複製代碼
  1. 打開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); //可以直接添加輸入流
複製代碼
  1. 等待連接服務器
HttpClientResponse response = await request.close();
複製代碼

這一步完成後,請求信息就已經發送給服務器了,返回一個HttpClientResponse對象,它包含響應頭(header)和響應流(響應體的Stream),接下來就可以通過讀取響應流來獲取響應內容。

  1. 讀取響應內容
String responseBody = await response.transform(utf8.decoder).join();
複製代碼

我們通過讀取響應流來獲取服務器返回的數據,在讀取時我們可以設置編碼格式,這裏是utf8。

  1. 請求結束,關閉HttpClient
httpClient.close();
複製代碼

關閉client後,通過該client發起的所有請求都會中止。

  以上的步驟是dart原生網絡HttpClient使用方式,可以發現直接使用HttpClient發起網絡請求是比較麻煩的,很多事情都得手動處理,如果再涉及到文件上傳/下載、Cookie管理等就會變得非常繁瑣,並且HttpClient本身功能較弱,很多常用功能都不支持。

Dio庫

dio是一個強大的Dart Http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載、超時等...

  1. pubspec.yaml 添加依賴

        dependencies: dio: ^x.x.x #請使用pub上的最新版本

  1. 導入引用並創建dio 實例

import 'package:dio/dio.dart';
Dio dio = Dio();

接下來就可以通過 dio實例來發起網絡請求了,注意,一個dio實例可以發起多個http請求,一般來說,APP只有一個http數據源時,dio應該使用單例模式。

  1. 發起網絡請求

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章