什么是 dio
dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。
实战:HTTP 网络请求封装
dart
class HttpUtil {
late Dio _dio;
// 单例模式
static final HttpUtil _instance = HttpUtil._internal();
factory HttpUtil() => _instance;
HttpUtil._internal() {
_dio = Dio(
BaseOptions(
baseUrl: AppConst.baseUrl,
headers: {
'platformId': AppConst.platformId,
'tenant-id': 1,
}
)
);
_dio.interceptors.add(LogInterceptor(responseBody: true));
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options, RequestInterceptorHandler handler) async {
if (options.headers['Authorization'] == null && UserStorage.getAccessToken() != null) {
options.headers['Authorization'] = 'Bearer ${UserStorage.getAccessToken()}';
}
return handler.next(options);
},
onResponse: (Response<dynamic> response, ResponseInterceptorHandler handler) async {
if (response.data['code'] == 0 || response.data['code'] == "00000") {
} else if (response.data['code'] == 401) {
if (UserStorage.getRefreshToke() != null && !response.requestOptions.path.contains('/auth/refresh')) {
ApiResponse<Token> newToken = await _refreshToken(UserStorage.getRefreshToke()!);
UserStorage.setToken(newToken.data!);
response.requestOptions.headers['Authorization'] = 'Bearer ${newToken.data?.accessToken}';
_dio.options.headers['Authorization'] = 'Bearer ${newToken.data?.accessToken}';
final reResponse = await _dio.fetch(response.requestOptions);
return handler.resolve(reResponse);
} else {
_dio.options.headers['Authorization'] = null;
UserStorage.clearAll();
}
} else {
// todo toast 展示
}
return handler.next(response);
},
onError: (DioException error, ErrorInterceptorHandler handler) async {
return handler.next(error);
},
));
}
Future<T> get<T> ( String path, { Map<String, dynamic>? params, Options? options, CancelToken? cancelToken} ) async {
try {
final response = await _dio.get(
path,
queryParameters: params,
options: options,
cancelToken: cancelToken,
);
return response.data;
} catch (e) {
throw ApiException(e.toString());
}
}
Future<T> post <T> ( String path, { dynamic data, Map<String, dynamic>? params, Options? options, CancelToken? cancelToken,}) async {
try {
final response = await _dio.post(
path, data:
data,
queryParameters: params,
options: options,
cancelToken: cancelToken,
);
return response.data;
} catch (e) {
throw ApiException(e.toString());
}
}
}
使用示例
javascript
static Future<ApiResponse<Token>> _refreshToken(String refreshToken) async {
final response = await HttpUtil().post('/system/auth/refresh-token',
params: { 'refreshToken': refreshToken} );
return ApiResponse<Token>.fromJson(response, (json) => Token.fromJson(json));
性能优化建议
-
合理使用拦截器
- 避免在拦截器中进行耗时操作
- 使用异步操作时注意处理异常
-
请求优化
- 合理设置超时时间
- 使用cancelToken取消不必要的请求
- 避免频繁的重复请求
-
缓存策略
- 针对不常变化的数据实现缓存
- 设置合理的缓存过期时间
- 考虑使用本地存储持久化缓存
-
错误处理
- 实现统一的错误处理机制
- 合理使用重试机制
- 提供友好的错误提示
FAQ【干货】
- 证书验证问题
ini
// 忽略证书验证
dio.options.validateStatus = (status) {
return status! < 500;
};
// 或者自定义证书验证
(dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate =
(HttpClient client) {
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
return client;
};
- 请求取消后的内存泄露
scss
// 在dispose时取消所有请求
final _cancelTokens = <CancelToken>[];
void addCancelToken(CancelToken token) {
_cancelTokens.add(token);
}
@override
void dispose() {
for (final token in _cancelTokens) {
token.cancel();
}
_cancelTokens.clear();
super.dispose();
}