Flutter用dio封装http网络请求,设置统一的请求地址、headers及处理返回内容
封装http请求是项目中经常需要做的,常用于设置通用请求地址、请求headers以及处理返回结果,例如在项目中开发地址、测试地址、上线地址是不一样的,当在封装的请求设置好默认地址之后只需要改一个地址而不需要每一个接口都去修改,以及统一在headers设置token用来校验身份等。
先来看一下完成后的使用方法把(格式是不是有点像ajax)
HttpUtil.get( url, data: { key: value }, headers: { key: value } success: (data){ // 请求成功返回的数据 },error: (errorMsg){ // 请求失败返回的错误信息 } );
封装实现
使用dio先在pubspec.yaml添加dio包,然后packages get,获取最新的版本https://pub.dev/packages/dio
dio: ^2.1.7
然后在封装的地方引用它
import 'package:dio/dio.dart';
在页面中先设置一个请求地址
class BaseUrl{ // 配置默认请求地址 static const String url = 'http://127.0.0.1'; }
再建立一个HttpUtil类,把请求方法写在里面,先来实现一下get请求,在方法中写好可接收的参数,url必填其他选填
class HttpUtil{ static void get( String url, { Map<String, dynamic> data, Map<String, dynamic> headers, Function success, Function error } ) async { // 对接收到的请求参数进行从处理 } }
在get请求中携带参数的用字符串拼接的形式例如:http://127.0.0.1/vod/getPlayInfo?userid=bd2439487ce6540a68ce01b8f57242eb6&number=5,所以在接收到请求携带的参数后如果请求参数不为空需要对携带的参数其进行拼接处理
// 数据拼接 if(data != null && data.isNotEmpty){ StringBuffer options= new StringBuffer('?'); data.forEach((key, value){ options.write('${key}=${value}&'); }); String optionsStr = options.toString(); optionsStr = optionsStr.substring(0, optionsStr.length - 1); url += optionsStr; }
数据拼接完成之后即可发起请求,把请求写到一个_sendRequest方法中,然后进行进一步的处理
// 发送get请求 await _sendRequest( url, 'get', success, headers: headers, error: error );
在写_sendRequest方法之前先看后端返回格式,我这边的返回格式是这样的,当请求成功后端code返回0,失败会返回其他失败码及提示信息
{ "code": 0, // int "msg": "后端返回失败提示信息", // string "data": {} // 这可能是对象,也可能数数组 }
下面来实现_sendRequest方法,当获取到请求的url后进行判断,如果url是以http开头的就可认为其是完整地址,那就直接使用传过来的地址,如果不是以http开头的,那就使用设置的默认地址来拼接请求地址
// 请求处理 static Future _sendRequest( String url, String method, Function success, { Map<String, dynamic> data, Map<String, dynamic> headers, Function error } ) async { int _code; String _msg; var _backData; // 检测请求地址是否是完整地址 if(!url.startsWith('http')){ url = BaseUrl.url + url; } }
发起请求
在请求之前先判断data和headers是否为空,然后配置dio的请求信息,查看dio更多API:https://github.com/flutterchina/dio
try{ Map<String, dynamic> dataMap = data == null ? new Map() : data; Map<String, dynamic> headersMap = headers == null ? new Map() : headers; // 配置dio请求信息 Response response; Dio dio = new Dio(); dio.options.connectTimeout = 10000; // 服务器链接超时,毫秒 dio.options.receiveTimeout = 3000; // 响应流上前后两次接受到数据的间隔,毫秒 dio.options.headers.addAll(headersMap); // 添加headers,如需设置统一的headers信息也可在此添加 if(method == 'get'){ response = await dio.get(url); }else{ response = await dio.post(url,data: dataMap); } }catch(exception){ _handError(error, '数据请求错误:'+exception.toString()); }
请求结果处理
先判断请求状态是否为200,否则返回请求错误;然后看后端返回的code是否为0,不为0则返回状态吗+错误信息
if(response.statusCode != 200){ _msg = '网络请求错误,状态码:' + response.statusCode.toString(); _handError(error, _msg); return; } // 返回结果处理 Map<String, dynamic> resCallbackMap = response.data; _code = resCallbackMap['code']; _msg = resCallbackMap['msg']; _backData = resCallbackMap['data']; if(success != null){ if(_code == 0){ success(_backData); }else{ String errorMsg = _code.toString()+':'+_msg; _handError(error, errorMsg); } }
最后再写一个返回错误信息的方法
// 返回错误信息 static Future _handError(Function errorCallback,String errorMsg){ if(errorCallback != null){ errorCallback(errorMsg); } }
完成后测试一下一个get请求,测试的json如下
{ "code": 0, "msg": "", "data": { "backResult": "返回成功啦" } }
请求代码
HttpUtil.get( '/app_model/httptest.json', success: (data){ print(data.toString()); },error: (errorMsg){ print(errorMsg); } );
返回结果如下:
I/flutter ( 7871): {backResult: 返回成功啦}
完整代码
源文件可以到git获取https://gitee.com/daydayfull/flutter_httputil
import 'package:dio/dio.dart'; class BaseUrl{ // 配置默认请求地址 static const String url = 'http://127.0.0.1'; } class HttpUtil{ static void get( String url, { Map<String, dynamic> data, Map<String, dynamic> headers, Function success, Function error } ) async { // 数据拼接 if(data != null && data.isNotEmpty){ StringBuffer options= new StringBuffer('?'); data.forEach((key, value){ options.write('${key}=${value}&'); }); String optionsStr = options.toString(); optionsStr = optionsStr.substring(0, optionsStr.length - 1); url += optionsStr; } // 发送get请求 await _sendRequest( url, 'get', success, headers: headers, error: error ); } static void post( String url, { Map<String, dynamic> data, Map<String, dynamic> headers, Function success, Function error } ) async { // 发送post请求 _sendRequest( url, 'post', success, data: data, headers: headers, error: error ); } // 请求处理 static Future _sendRequest( String url, String method, Function success, { Map<String, dynamic> data, Map<String, dynamic> headers, Function error } ) async { int _code; String _msg; var _backData; // 检测请求地址是否是完整地址 if(!url.startsWith('http')){ url = BaseUrl.url + url; } try{ Map<String, dynamic> dataMap = data == null ? new Map() : data; Map<String, dynamic> headersMap = headers == null ? new Map() : headers; // 配置dio请求信息 Response response; Dio dio = new Dio(); dio.options.connectTimeout = 10000; // 服务器链接超时,毫秒 dio.options.receiveTimeout = 3000; // 响应流上前后两次接受到数据的间隔,毫秒 dio.options.headers.addAll(headersMap); // 添加headers,如需设置统一的headers信息也可在此添加 if(method == 'get'){ response = await dio.get(url); }else{ response = await dio.post(url,data: dataMap); } if(response.statusCode != 200){ _msg = '网络请求错误,状态码:' + response.statusCode.toString(); _handError(error, _msg); return; } // 返回结果处理 Map<String, dynamic> resCallbackMap = response.data; _code = resCallbackMap['code']; _msg = resCallbackMap['msg']; _backData = resCallbackMap['data']; if(success != null){ if(_code == 0){ success(_backData); }else{ String errorMsg = _code.toString()+':'+_msg; _handError(error, errorMsg); } } }catch(exception){ _handError(error, '数据请求错误:'+exception.toString()); } } // 返回错误信息 static Future _handError(Function errorCallback,String errorMsg){ if(errorCallback != null){ errorCallback(errorMsg); } } }
这里只简单写了get和post两个方法,在dio中提供了如下方法
Future get(...) Future post(...) Future put(...) Future delete(...) Future head(...) Future put(...) Future path(...) Future download(...)