一、封装网络工具类的注意点
0.单例模式
工具类用的很频繁,所以不可能每次用到的地方都创造一个实例,那会浪费大量内存。全局共享一个单例就行了。
1.常用请求方法封装
get/post/put/delete/上传文件/下载文件等常用请求方法
2.全局配置:请求参数和头部处理
配置全局的headers
配置baseurl
headers需要动态可添加
3.拦截器(请求和响应拦截)
- 请求拦截:在发送请求之前可以进行统一的处理,例如添加认证信息、日志记录、请求调试等。(在这里进行token的添加,和token刷新机制的处理)
- 响应拦截:可以在收到响应后进行统一处理,例如统一错误处理、日志记录等。
4.响应处理
- 响应解析:封装统一的响应格式,解析响应内容,如JSON、XML、文本等。
- 状态码处理:针对不同的HTTP状态码(如200、400、404、500等)进行处理,提供友好的错误提示。
- 超时处理:可以设置请求超时时间,并根据超时情况进行处理(例如重新请求、返回超时错误等)。
5.日志记录
- 请求/响应日志:为开发者提供请求和响应的日志信息,帮助调试和分析。 (注意这里日志默认输出就是一行,如果内容太长,中间会被省略掉。所以需要自定义一下日志打印, 比如我要定义如下格式日志: 时间 日志级别 日志内容, 日志内容过长需要分成多段打印。)
- 调试模式:在开发过程中,可以开启调试模式,输出详细的请求、响应数据等信息。
6.错误处理
二、代码如下
http_config.dart
enum Env {
dev,
pre,
prd;
static Env current = dev;
}
extension BaseUrl on Env {
String get baseUrl {
switch (this) {
case Env.dev:
return 'www.dev-env.com';
case Env.pre:
return 'www.pre-env.com';
case Env.prd:
return 'www.prd-env.com';
default:
return 'www.dev-env.com';
}
}
}
http_inteceptors.dart
import 'package:dio/dio.dart';
class RequestInterceptor extends Interceptor {
Future<String> getToken() async {
return 'token';
}
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
super.onRequest(options, handler);
//请求发送前添加逻辑
options.headers['token'] = await getToken();
//继续执行请求
return handler.next(options);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
// TODO: implement onError
super.onError(err, handler);
// 当访问令牌过期时,受保护的资源会返回401 Unauthorized错误。
// 此时,客户端需要使用刷新令牌获取新的访问令牌。
//TODO: token刷新机制
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
// TODO: implement onResponse
super.onResponse(response, handler);
}
}
http_manager.dart
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:http/src/http_interceptors.dart';
import 'http_config.dart';
class HttpManager {
static String val = '';
static late Dio _dio;
//单例模式
static late final HttpManager _httpManager = HttpManager._internal();
factory HttpManager.shared() => _httpManager;
HttpManager._internal() {
_dio = Dio();
//全局配置,请求参数和头部处理
_dio.options = BaseOptions(
baseUrl: Env.current.baseUrl,
headers: {'app-name': 'test-app'},
connectTimeout: const Duration(milliseconds: 5000),
receiveTimeout: const Duration(milliseconds: 5000),
);
//拦截器(这里只进行请求拦截)
_dio.interceptors.add(RequestInterceptor());
//TODO: 这里加个日志拦截器专门对日志进行处理
//TODO: 或者把token刷新的逻辑提取出来,专门的token刷新机制进行处理
//但是要确定拦截器是并发的还是顺序执行的
}
Future<Map<String, dynamic>?> request(
String path, {
String? method,
Object? data,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? extra,
Map<String, dynamic>? headers,
}) async {
try {
Response response = await _dio.request(
path,
data: data,
queryParameters: queryParameters,
options: Options(
method: method,
headers: headers,
extra: extra,
),
);
//TODO:日志记录
//这里对响应进行处理
return _handleResponse(response);
} on DioException catch (e) {
//异常处理
return _handleException(e);
}
}
Future<Map<String, dynamic>?> get(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? extra,
Map<String, dynamic>? headers,
}) async {
Map<String, dynamic>? response = await request(
path,
data: data,
queryParameters: queryParameters,
method: 'GET',
extra: extra,
headers: headers,
);
return response;
}
Future<Map<String, dynamic>?> post(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? extra,
Map<String, dynamic>? headers,
}) async {
Map<String, dynamic>? response = await request(
path,
data: data,
queryParameters: queryParameters,
method: 'POST',
extra: extra,
headers: headers,
);
return response;
}
Future<Map<String, dynamic>?> put(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? extra,
Map<String, dynamic>? headers,
}) async {
Map<String, dynamic>? response = await request(
path,
data: data,
queryParameters: queryParameters,
method: 'PUT',
extra: extra,
headers: headers,
);
return response;
}
Future<Map<String, dynamic>?> delete(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Map<String, dynamic>? extra,
Map<String, dynamic>? headers,
}) async {
Map<String, dynamic>? response = await request(
path,
data: data,
queryParameters: queryParameters,
method: 'DElETE',
extra: extra,
headers: headers,
);
return response;
}
///处理请求结果
//这一步可以在onResponse拦截进行预处理,看着办。
Map<String, dynamic>? _handleResponse(Response response) {
dynamic responseData;
//用户只对data字段感兴趣
if (response.data != null &&
response.data is Map &&
response.data['data'] != null) {
responseData = response.data['data'];
//如果这里有服务端自定义的错误码如response.data['code'];
//可以依据code进行业务层的错误处理
}
return responseData;
}
///处理异常
Map<String, dynamic>? _handleException(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
//连接超时处理
break;
case DioExceptionType.sendTimeout:
//发送超时处理
break;
case DioExceptionType.receiveTimeout:
//接收超时处理
break;
case DioExceptionType.badResponse:
//服务器响应错误处理处理
break;
case DioExceptionType.cancel:
//请求取消处理
break;
case DioExceptionType.connectionError:
//连接错误处理
break;
case DioExceptionType.unknown:
default:
//未知错误
}
return null;
}
}
github源码
参考文档
这边文章写的非常详细: bbs.huaweicloud.com/blogs/42280… 日志记录 bbs.huaweicloud.com/blogs/44306…
如果有帮到你的话😂顺手给个打赏吧。身体健康,财源滚滚~.