Flutter之网络请求
Flutter之网络请求
一,介绍与需求
1.1,介绍
1,http一个可组合的,基于Future的库,用于发出HTTP请求。包含一组高级功能和类,可轻松使用HTTP资源。它与平台无关,可以在命令行和浏览器上使用。
2,Dart的功能强大的Http客户端,支持拦截器,全局配置,FormData,请求取消,文件下载,超时等。
1.2,需求
编写一个 App,最离不开的就是网络请求了。目前Flutter普及率也不是很高,网络请求大致分为如下三种方式:
- Dart 原生的网络请求
HttpClient
- 库
http
- 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,返回的结果
下一章->待定