赠书|Flutter-dio封装,满足你的需要
作者 | 艾维码 地址 | juejin.im/post/5ee5d2c1e51d45788619dcc2
前言
dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等..
基本使用
添加依赖
dependencies:
dio: ^3.x.x // 请使用pub上3.0.0分支的最新版本
发起一个 GET 请求 :
Response response;
Dio dio = Dio();
response = await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
// 请求参数也可以通过对象传递,上面的代码等同于:
response = await dio.get("/test", queryParameters: {"id": 12, "name": "wendu"});
print(response.data.toString());
发起一个 POST 请求 :
response = await dio.post("/test", data: {"id": 12, "name": "wendu"});
发起多个并发请求:
response = await Future.wait([dio.post("/info"), dio.get("/token")]);
下载文件:
response = await dio.download("https://www.google.com/", "./xx.html");
发送 FormData:
FormData formData = FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData);
通过FormData上传多个文件:
FormData.fromMap({
"name": "wendux",
"age": 25,
"file": await MultipartFile.fromFile("./text.txt",filename: "upload.txt"),
"files": [
await MultipartFile.fromFile("./text1.txt", filename: "text1.txt"),
await MultipartFile.fromFile("./text2.txt", filename: "text2.txt"),
]
});
response = await dio.post("/info", data: formData);
封装Dio
为什么要封装 dio
上面看了dio的api,非常灵活和简单,那么为什么还要封装呢?因为我们开发需要统一的配置场景。比如:
全局token验证 自定义拦截器 缓存处理 统一封装业务错误逻辑 代理配置 重试机制 log输出
代理配置:
flutter抓包需要配置dio代理,所以我们实现一个代理配置,proxy.dart:
// 是否启用代理
const PROXY_ENABLE = false;
/// 代理服务IP
// const PROXY_IP = '192.168.1.105';
const PROXY_IP = '172.16.43.74';
/// 代理服务端口
const PROXY_PORT = 8866;
错误处理:
一般错误分为网络错误、请求错误、认证错误、服务器错误,所以实现统一的错误处理,认证错误需要登录等认证,所以单独一个类型,请求错误也单独设置一个类型,方便我们定位错误,app_exceptions.dart:
import 'package:dio/dio.dart';
/// 自定义异常
class AppException implements Exception {
final String _message;
final int _code;
AppException([
this._code,
this._message,
]);
String toString() {
return "$_code$_message";
}
factory AppException.create(DioError error) {
switch (error.type) {
case DioErrorType.CANCEL:
{
return BadRequestException(-1, "请求取消");
}
break;
case DioErrorType.CONNECT_TIMEOUT:
{
return BadRequestException(-1, "连接超时");
}
break;
case DioErrorType.SEND_TIMEOUT:
{
return BadRequestException(-1, "请求超时");
}
break;
case DioErrorType.RECEIVE_TIMEOUT:
{
return BadRequestException(-1, "响应超时");
}
break;
case DioErrorType.RESPONSE:
{
try {
int errCode = error.response.statusCode;
// String errMsg = error.response.statusMessage;
// return ErrorEntity(code: errCode, message: errMsg);
switch (errCode) {
case 400:
{
return BadRequestException(errCode, "请求语法错误");
}
break;
case 401:
{
return UnauthorisedException(errCode, "没有权限");
}
break;
case 403:
{
return UnauthorisedException(errCode, "服务器拒绝执行");
}
break;
case 404:
{
return UnauthorisedException(errCode, "无法连接服务器");
}
break;
case 405:
{
return UnauthorisedException(errCode, "请求方法被禁止");
}
break;
case 500:
{
return UnauthorisedException(errCode, "服务器内部错误");
}
break;
case 502:
{
return UnauthorisedException(errCode, "无效的请求");
}
break;
case 503:
{
return UnauthorisedException(errCode, "服务器挂了");
}
break;
case 505:
{
return UnauthorisedException(errCode, "不支持HTTP协议请求");
}
break;
default:
{
// return ErrorEntity(code: errCode, message: "未知错误");
return AppException(errCode, error.response.statusMessage);
}
}
} on Exception catch (_) {
return AppException(-1, "未知错误");
}
}
break;
default:
{
return AppException(-1, error.message);
}
}
}
}
/// 请求错误
class BadRequestException extends AppException {
BadRequestException([int code, String message]) : super(code, message);
}
/// 未认证异常
class UnauthorisedException extends AppException {
UnauthorisedException([int code, String message]) : super(code, message);
}
Error拦截器:
有了上面的异常类型,我们要把DioError变成自己定义的异常:
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'app_exceptions.dart';
/// 错误处理拦截器
class ErrorInterceptor extends Interceptor {
@override
Future onError(DioError err) {
// error统一处理
AppException appException = AppException.create(err);
// 错误提示
debugPrint('DioError===: ${appException.toString()}');
err.error = appException;
return super.onError(err);
}
}
增加取消功能:
/*
* 取消请求
*
* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
* 所以参数可选
*/
void cancelRequests({CancelToken token}) {
token ?? _cancelToken.cancel("cancelled");
}
增加认证header:
/// 读取本地配置
Map<String, dynamic> getAuthorizationHeader() {
var headers;
String accessToken = Global.accessToken;
if (accessToken != null) {
headers = {
'Authorization': 'Bearer $accessToken',
};
}
return headers;
}
添加cookie和cache:
添加cookie管理:
cookie_jar: ^1.0.1
dio_cookie_manager: ^1.0.0
// Cookie管理
CookieJar cookieJar = CookieJar();
dio.interceptors.add(CookieManager(cookieJar));
// 加内存缓存
dio.interceptors.add(NetCache());
利用sp做磁盘缓存: shared_preferences: ^0.5.6+3
dio加入缓存:
// 加内存缓存
dio.interceptors.add(NetCacheInterceptor());
重试拦截器:
在网络断开的时候,监听网络,等重连的时候重试:
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'connectivity_request_retrier.dart';
class RetryOnConnectionChangeInterceptor extends Interceptor {
final DioConnectivityRequestRetrier requestRetrier;
RetryOnConnectionChangeInterceptor({
@required this.requestRetrier,
});
@override
Future onError(DioError err) async {
if (_shouldRetry(err)) {
try {
return requestRetrier.scheduleRequestRetry(err.request);
} catch (e) {
return e;
}
}
return err;
}
bool _shouldRetry(DioError err) {
return err.type == DioErrorType.DEFAULT &&
err.error != null &&
err.error is SocketException;
}
}
添加重试拦截器:
if (Global.retryEnable) {
dio.interceptors.add(
RetryOnConnectionChangeInterceptor(
requestRetrier: DioConnectivityRequestRetrier(
dio: Dio(),
connectivity: Connectivity(),
),
),
);
}
使用
初始化:
void main() {
HttpUtils.init(
baseUrl: "https://gan.io/",
);
runApp(MyApp());
}
在model里写接口请求:
static Future<ApiResponse<CategoryEntity>> getCategories() async {
try {
final response = await HttpUtils.get(categories);
var data = CategoryEntity.fromJson(response);
return ApiResponse.completed(data);
} on DioError catch (e) {
return ApiResponse.error(e.error);
}
}
调用:
void getCategories() async {
ApiResponse<CategoryEntity> entity = await GanRepository.getCategories();
print(entity.data.data.length);
}
第十四期
今天联合机械工业出版社华章公司送出三本书籍《Flutter实战入门》
「推荐语」作者手把手带领读者进入Flutter开发世界,方法简单,效果明显,凝聚了作者多年的实际项目经验。
「自费购书链接」
具体规则是这样的:
转发至朋友圈,是绝对的真爱
让我知道你在看