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,返回的结果

下一章->待定

 

posted @ 2019-11-29 10:46  jackson影琪  阅读(1689)  评论(1编辑  收藏