1. 程式人生 > >Flutter之網路請求

Flutter之網路請求

Flutter之網路請求

一,介紹與需求

1.1,介紹

1,http一個可組合的,基於Future的庫,用於發出HTTP請求。包含一組高階功能和類,可輕鬆使用HTTP資源。它與平臺無關,可以在命令列和瀏覽器上使用。

2,Dart的功能強大的Http客戶端,支援攔截器,全域性配置,FormData,請求取消,檔案下載,超時等。

1.2,需求

編寫一個 App,最離不開的就是網路請求了。目前Flutter普及率也不是很高,網路請求大致分為如下三種方式:
  1. Dart 原生的網路請求 HttpClient
  2. 庫 http
  3. Flutter中文網釋出的 dio

本文主要介紹後面兩種網路請求方式的封裝與使用,dart的原生網路請求HttpClient可參考文件通過HttpClient發起HTTP請求

二,網路請求封裝

第一步:新增依賴

開啟 pubspec.yaml檔案,在dependencies下新增如下包:

1 http: ^0.12.0+2
2 dio: ^3.0.4

儲存後,一般會自動下載包;如果沒有自動下載可在專案根目錄下執行如下命令,進行下載:

1 flutter packages get

2.1,http請求庫

第二步:引入包並建立網路請求類

1 import 'package:http/http.dart' as http;
1 class NetUtils {
2 
3 ...
4 
5 }

第三步:get方式請求

 1 // get請求的封裝,傳入的兩個引數分別是請求URL和請求引數,請求引數以map的形式傳入,會在方法體中自動拼接到URL後面
 2   static Future<String> get(String url, {Map<String, String> params}) async {
 3     if (params != null && params.isNotEmpty) {
 4       // 如果引數不為空,則將引數拼接到URL後面
 5       StringBuffer sb = StringBuffer("?");
 6       params.forEach((key, value) {
 7         sb.write("$key" + "=" + "$value" + "&");
 8       });
 9       String paramStr = sb.toString();
10       paramStr = paramStr.substring(0, paramStr.length - 1);
11       url += paramStr;
12     }
13     http.Response res = await http.get(url, headers: getCommonHeader());
14     return res.body;
15   }

第四步:POST方式請求

1   // post請求
2   static Future<String> post(String url, {Map<String, String> params}) async {
3     http.Response res = await http.post(url, body: params, headers: getCommonHeader());
4     print(res.statusCode);
5     return res.body;
6   }

其他請求方式與post方式類似,這兒就不一一列舉其他請求方式了。

第五步:統一傳參處理

1   static Map<String, String> getCommonHeader() {
2     Map<String, String> header = Map();
3     header['yingqi'] = "jackson影琪";
4     return header;
5   }

第六步:完整程式碼

 1 import 'dart:async';
 2 import 'package:http/http.dart' as http;
 3 
 4 class NetUtils {
 5   // get請求的封裝,傳入的兩個引數分別是請求URL和請求引數,請求引數以map的形式傳入,會在方法體中自動拼接到URL後面
 6   static Future<String> get(String url, {Map<String, String> params}) async {
 7     if (params != null && params.isNotEmpty) {
 8       // 如果引數不為空,則將引數拼接到URL後面
 9       StringBuffer sb = StringBuffer("?");
10       params.forEach((key, value) {
11         sb.write("$key" + "=" + "$value" + "&");
12       });
13       String paramStr = sb.toString();
14       paramStr = paramStr.substring(0, paramStr.length - 1);
15       url += paramStr;
16     }
17     http.Response res = await http.get(url, headers: getCommonHeader());
18     return res.body;
19   }
20 
21   // post請求
22   static Future<String> post(String url, {Map<String, String> params}) async {
23     http.Response res = await http.post(url, body: params, headers: getCommonHeader());
24     print(res.statusCode);
25     return res.body;
26   }
27 
28  // put請求
29   static Future<String> put(String url, {Map<String, String> params}) async {
30     http.Response res = await http.put(url, body: params, headers: getCommonHeader());
31     return res.body;
32   }
33 
34   static Map<String, String> getCommonHeader() {
35     Map<String, String> header = Map();
36     header['yingqi'] = "1";
37     return header;
38   }
39 
40 }

2.2,Dio

第二步:引入包並建立網路請求類

1 import 'dart:async';
2 import 'package:dio/dio.dart';
3 
4 class DioNetUtils {
5 
6 ...
7 
8 }

第三步:初始化Dio

 1   static final DioNetUtils _singleton = DioNetUtils._init();
 2   static Dio _dio;
 3 
 4  DioNetUtils._init() {
 5     BaseOptions options = new BaseOptions(
 6       baseUrl: "http://192.168.1.19:8880",
 7       connectTimeout: 1000 * 1,
 8       receiveTimeout: 1000 * 2,
 9       //Http請求頭.
10       headers: {//可統一配置傳參
11         //do something
12         "version": "1.0.0"
13       },
14       //請求的Content-Type,預設值是"application/json; charset=utf-8". 也可以用"application/x-www-form-urlencoded"
15       // contentType: "application/json; charset=utf-8",
16       //表示期望以那種格式(方式)接受響應資料。接受4種類型 `json`, `stream`, `plain`, `bytes`. 預設值是 `json`,
17       responseType: ResponseType.json,
18     );
19     _dio = Dio(options);
20 
21 }

第四步:新增攔截器

 1  //新增攔截器
 2     _dio.interceptors
 3         .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
 4       print("請求之前處理");
 5       return options; //continue
 6     }, onResponse: (Response response) {
 7       print("響應之前處理");
 8       print(options);
 9       return response; // continue
10     }, onError: (DioError e) {
11       print("錯誤之前提示");
12       Response errorInfo = _dealErrorInfo(e);
13       return errorInfo; //continue
14     }));

第五步:統一處理錯誤資訊

 1  _dealErrorInfo(error) {
 2     print(error.type);
 3     // 請求錯誤處理
 4     Response errorResponse;
 5     if (error.response != null) {
 6       errorResponse = error.response;
 7     } else {
 8       errorResponse = new Response(statusCode: 201);
 9     }
10     // 請求超時
11     if (error.type == DioErrorType.CONNECT_TIMEOUT) {
12      ShowToast.warning("網路請求超時,請稍後重試");
13       errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
14     }
15     // 請求連線超時
16     else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
17       ShowToast.warning("網路連線超時,請稍後重試");
18       errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
19     }
20     // 伺服器錯誤
21     else if (error.type == DioErrorType.RESPONSE) {
22      ShowToast.warning("伺服器繁忙,請稍後重試");
23       errorResponse.statusCode = ResultCode.RESPONSE;
24     }
25     // 一般伺服器錯誤
26     else {
27       ShowToast.warning("網路連線不可用,請稍後重試1");
28       errorResponse.statusCode = ResultCode.DEFAULT;
29     }
30     return errorResponse;
31   }

第六步:GET方式請求

 1   /// Make http request with options.
 2   /// [method] The request method.
 3   /// [path] The url path.
 4   /// [data] The request data
 5   /// [options] The request options.
 6   /// String 返回 json data .
 7   Future<Map> request<T>(
 8     String path, {
 9     String method = Method.get,
10     String contentType= "application/json; charset=utf-8",
11     queryParameters,
12     Options options,
13     // CancelToken cancelToken,
14   }) async {
15     print('path===' + path);
16     Response response = await _dio.request(
17         path,
18         queryParameters: queryParameters,
19         options: _checkOptions(method, contentType, options),
20         // cancelToken: cancelToken,
21       );
22     _printHttpLog(response);
23     if (response.statusCode == 200) {
24       try {
25         if (response.data is Map) {
26           if (response.data["httpCode"] != 200) {
27            ShowToast.warning(response.data["message"]);
28             return new Future.error(new DioError(
29               response: response,
30               type: DioErrorType.RESPONSE,
31             ));
32           }
33           // 由於不同的介面返回的格式不固定不規範,所以需要根據介面格式自定義.
34           return response.data['data'];
35         } else {
36           if (response.data is List) {
37             Map<String, dynamic> _dataMap = Map();
38             _dataMap["data"] = response.data;
39             return _dataMap;
40           }
41         }
42       } catch (e) {
43         ShowToast.warning("網路連線不可用,請稍後重試");
44         return new Future.error(new DioError(
45           response: response,
46           // message: "data parsing exception...",
47           type: DioErrorType.RESPONSE,
48         ));
49       }
50     }
51      ShowToast.warning("網路連線不可用,請稍後重試");
52     return new Future.error(new DioError(
53       response: response,
54       type: DioErrorType.RESPONSE,
55     ));
56   }

第七步:POST方式請求-json傳值

 1  /// Make http request with options.
 2   /// [method] The request method.
 3   /// [path] The url path.
 4   /// [data] The request data
 5   /// [options] The request options.
 6   /// String 返回 json data .
 7   Future<Map> request<T>(
 8     String path, {
 9     String method = Method.get,
10     String contentType= "application/json; charset=utf-8",
11     queryParameters,
12     Options options,
13     // CancelToken cancelToken,
14   }) async {
15     print('path===' + path);
16     Response response;
17     if (method == Method.get) {
18       //GET方式
19 
20      ...
21 
22     } else {
23       //除GET的其他方式
24       var requestData = queryParameters;
25       response = await _dio.request(
26         path,
27         data: requestData,
28         options: _checkOptions(method, contentType, options),
29         // cancelToken: cancelToken,
30       );
31     }
32 
33     _printHttpLog(response);
34     if (response.statusCode == 200) {
35 
36       ...
37 
38     }
39    
40     ...
41 
42   }

第八步:POST方式請求-表單傳值

1       //if (contentType == 'application/x-www-form-urlencoded') {//表單方式
2        var requestData = new FormData.fromMap({
3           "name": "jackson影琪",
4           "age": 25,
5         });

第九步:請求日誌處理

 1  // print Http Log.
 2   void _printHttpLog(Response response) {
 3     print(!_isDebug);
 4     if (!_isDebug) {
 5       return;
 6     }
 7     try {
 8       print("----------------Http Log Start----------------" +
 9           _getOptionsStr(response.request));
10       print(response);
11       print("----------------Http Log end----------------");
12     } catch (ex) {
13       print("Http Log" + " error......");
14     }
15   }
16 
17   // get Options Str.
18   String _getOptionsStr(RequestOptions request) {
19     return "method: " +
20         request.method +
21         "  baseUrl: " +
22         request.baseUrl +
23         "  path: " +
24         request.path;
25   }

第10步:完整程式碼

  1 import 'dart:async';
  2 import 'package:dio/dio.dart';
  3 import 'ShowToastUtils.dart';
  4 
  5 class DioNetUtils {
  6   static final DioNetUtils _singleton = DioNetUtils._init();
  7   static Dio _dio;
  8 
  9   /// 是否是debug模式.
 10   static bool _isDebug = true;
 11 
 12   /// 開啟debug模式.
 13   static void openDebug() {
 14     _isDebug = true;
 15   }
 16 
 17   DioNetUtils._init() {
 18     BaseOptions options = new BaseOptions(
 19       baseUrl: "http://192.168.1.19:8880",
 20       connectTimeout: 1000 * 1,
 21       receiveTimeout: 1000 * 2,
 22       //Http請求頭.
 23       headers: {
 24         //do something
 25         "version": "1.0.0"
 26       },
 27       //請求的Content-Type,預設值是"application/json; charset=utf-8". 也可以用"application/x-www-form-urlencoded"
 28       // contentType: "application/json; charset=utf-8",
 29       //表示期望以那種格式(方式)接受響應資料。接受4種類型 `json`, `stream`, `plain`, `bytes`. 預設值是 `json`,
 30       responseType: ResponseType.json,
 31     );
 32     _dio = Dio(options);
 33     //新增攔截器
 34     _dio.interceptors
 35         .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
 36       print("請求之前處理");
 37       return options; //continue
 38     }, onResponse: (Response response) {
 39       print("響應之前處理");
 40       print(options);
 41       return response; // continue
 42     }, onError: (DioError e) {
 43       print("錯誤之前提示");
 44       Response errorInfo = _dealErrorInfo(e);
 45       return errorInfo; //continue
 46     }));
 47   }
 48 
 49   factory DioNetUtils() {
 50     return _singleton;
 51   }
 52 
 53   /// Make http request with options.
 54   /// [method] The request method.
 55   /// [path] The url path.
 56   /// [data] The request data
 57   /// [options] The request options.
 58   /// String 返回 json data .
 59   Future<Map> request<T>(
 60     String path, {
 61     String method = Method.get,
 62     String contentType= "application/json; charset=utf-8",
 63     queryParameters,
 64     Options options,
 65     // CancelToken cancelToken,
 66   }) async {
 67     print('path===' + path);
 68     Response response;
 69     if (method == Method.get) {
 70       //GET方式
 71       response = await _dio.request(
 72         path,
 73         queryParameters: queryParameters,
 74         options: _checkOptions(method, contentType, options),
 75         // cancelToken: cancelToken,
 76       );
 77     } else {
 78       //除GET的其他方式
 79       var requestData;
 80       print(contentType);
 81       if (contentType == 'application/x-www-form-urlencoded') {//表單方式
 82         requestData = new FormData.fromMap({
 83           "name": "jackson影琪",
 84           "age": 25,
 85         });
 86       }else{//json格式
 87          requestData = queryParameters;
 88       }
 89       response = await _dio.request(
 90         path,
 91         data: requestData,
 92         options: _checkOptions(method, contentType, options),
 93         // cancelToken: cancelToken,
 94       );
 95     }
 96 
 97     _printHttpLog(response);
 98     if (response.statusCode == 200) {
 99       try {
100         if (response.data is Map) {
101           if (response.data["httpCode"] != 200) {
102            ShowToast.warning(response.data["message"]);
103             return new Future.error(new DioError(
104               response: response,
105               type: DioErrorType.RESPONSE,
106             ));
107           }
108           // 由於不同的介面返回的格式不固定不規範,所以需要根據介面格式自定義.
109           return response.data['data'];
110         } else {
111           if (response.data is List) {
112             Map<String, dynamic> _dataMap = Map();
113             _dataMap["data"] = response.data;
114             return _dataMap;
115           }
116         }
117       } catch (e) {
118         ShowToast.warning("網路連線不可用,請稍後重試");
119         return new Future.error(new DioError(
120           response: response,
121           // message: "data parsing exception...",
122           type: DioErrorType.RESPONSE,
123         ));
124       }
125     }
126      ShowToast.warning("網路連線不可用,請稍後重試");
127     return new Future.error(new DioError(
128       response: response,
129       type: DioErrorType.RESPONSE,
130     ));
131   }
132 
133   /// check Options.
134   Options _checkOptions(method, contentType, options) {
135     if (options == null) {
136       options = new Options();
137     }
138     // if (contentType) {
139     //   //設定請求的型別 json 表單
140     //   options.contentType = contentType;
141     // }
142     options.method = method;
143     return options;
144   }
145 
146   // print Http Log.
147   void _printHttpLog(Response response) {
148     print(!_isDebug);
149     if (!_isDebug) {
150       return;
151     }
152     try {
153       print("----------------Http Log Start----------------" +
154           _getOptionsStr(response.request));
155       print(response);
156       print("----------------Http Log end----------------");
157     } catch (ex) {
158       print("Http Log" + " error......");
159     }
160   }
161 
162   // get Options Str.
163   String _getOptionsStr(RequestOptions request) {
164     return "method: " +
165         request.method +
166         "  baseUrl: " +
167         request.baseUrl +
168         "  path: " +
169         request.path;
170   }
171 
172 // 錯誤全域性處理
173   _dealErrorInfo(error) {
174     print(error.type);
175     // 請求錯誤處理
176     Response errorResponse;
177     if (error.response != null) {
178       errorResponse = error.response;
179     } else {
180       errorResponse = new Response(statusCode: 201);
181     }
182     // 請求超時
183     if (error.type == DioErrorType.CONNECT_TIMEOUT) {
184      ShowToast.warning("網路請求超時,請稍後重試");
185       errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
186     }
187     // 請求連線超時
188     else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
189       ShowToast.warning("網路連線超時,請稍後重試");
190       errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
191     }
192     // 伺服器錯誤
193     else if (error.type == DioErrorType.RESPONSE) {
194      ShowToast.warning("伺服器繁忙,請稍後重試");
195       errorResponse.statusCode = ResultCode.RESPONSE;
196     }
197     // 一般伺服器錯誤
198     else {
199       ShowToast.warning("網路連線不可用,請稍後重試1");
200       errorResponse.statusCode = ResultCode.DEFAULT;
201     }
202     return errorResponse;
203   }
204 }
205 
206 abstract class DioCallback<T> {
207   void onSuccess(T t);
208 
209   void onError(DioError error);
210 }

** dio網路請求失敗的回撥錯誤碼 **

 1 /*
 2  * dio網路請求失敗的回撥錯誤碼 自定義
 3  */
 4 class ResultCode {
 5   //正常返回是1
 6   static const SUCCESS = 1;
 7 
 8   //異常返回是0
 9   static const ERROR = 0;
10 
11   /// When opening  url timeout, it occurs.
12   static const CONNECT_TIMEOUT = -1;
13 
14   ///It occurs when receiving timeout.
15   static const RECEIVE_TIMEOUT = -2;
16 
17   /// When the server response, but with a incorrect status, such as 404, 503...
18   static const RESPONSE = -3;
19 
20   /// When the request is cancelled, dio will throw a error with this type.
21   static const CANCEL = -4;
22 
23   /// read the DioError.error if it is not null.
24   static const DEFAULT = -5;
25 }

** dio網路請求方式 **

1 /// 請求方法.
2 class Method {
3   static const String get = "GET";
4   static final String post = "POST";
5   static final String put = "PUT";
6   static final String head = "HEAD";
7   static final String delete = "DELETE";
8   static final String patch = "PATCH";
9 }

三,介面呼叫

3.1,http請求庫

1,頁面呼叫

 1  Map<String, String> params = Map();
 2       params['loginCode'] = _unameController.text;
 3       params['password'] = _pwdController.text;
 4  NetUtils.post(ServiceApi.loginAction, params: params).then((data) {
 5 print(ServiceApi.loginAction);
 6 print(data);
 7  }).catchError((e) {
 8         Toast.toast(
 9           context,
10           msg: '網路請求出錯:$e,請稍後重試!',
11           position: 'top',
12           bgColor: Color.fromRGBO(130, 0, 0, 1), // Color 提示框背景顏色
13           textColor: Color.fromRGBO(250, 100, 100, 1), // Color 提示框文字顏色
14         );
15       });

3.2,Dio

1,服務介面地址

 1 import 'dart:async';
 2 
 3 import '../util/DioNetUtils.dart';
 4 
 5 class ServiceNetApi {
 6   ///獲取使用者資訊
 7   Future<Map> getSingleDataById(data) async {
 8     return await DioNetUtils().request<String>(
 9       "/**/**/yingqi/**/getSingleDataById",
10       queryParameters: data,
11       method:Method.put
12     );
13   }
14 }

2,頁面呼叫

 1   void getData() async {
 2       Map<String, String> params = Map();
 3       params['Id'] = "123456789";
 4       params['Name'] = "jackson影琪";
 5     await ServiceNetApi().getSingleDataById(params).then((json) {
 6        print('getSingleDataById');
 7       print(json);
 8     }).catchError((e) {
 9 
10     });
11   }

3,返回的結果

下一章->待定

&n