252 lines
6.3 KiB
Dart
252 lines
6.3 KiB
Dart
import 'package:dio/dio.dart';
|
||
|
||
import 'log_utils.dart';
|
||
import 'sp_utils.dart';
|
||
|
||
class HttpUtil {
|
||
static HttpUtil? _instance;
|
||
|
||
factory HttpUtil({
|
||
required String baseUrl,
|
||
int timeout = 5,
|
||
}) {
|
||
_instance ??= HttpUtil._internal(
|
||
baseUrl: baseUrl,
|
||
timeout: timeout,
|
||
);
|
||
return _instance!;
|
||
}
|
||
|
||
final String baseUrl;
|
||
final int timeout;
|
||
late Dio _dio;
|
||
|
||
// 请求头配置
|
||
Map<String, dynamic> headers = {
|
||
'Content-Type': 'application/json;charset=UTF-8',
|
||
'Accept': 'application/json',
|
||
};
|
||
|
||
HttpUtil._internal({
|
||
required this.baseUrl,
|
||
required this.timeout,
|
||
}) {
|
||
_initDio();
|
||
}
|
||
|
||
void _initDio() {
|
||
BaseOptions options = BaseOptions(
|
||
baseUrl: baseUrl,
|
||
connectTimeout: Duration(seconds: timeout),
|
||
receiveTimeout: Duration(seconds: timeout),
|
||
sendTimeout: Duration(seconds: timeout),
|
||
headers: headers,
|
||
);
|
||
|
||
_dio = Dio(options);
|
||
|
||
// 添加请求拦截器
|
||
_dio.interceptors.add(
|
||
InterceptorsWrapper(
|
||
onRequest: (options, handler) {
|
||
logger.d("请求URL: ${options.uri}");
|
||
if (options.data != null) {
|
||
logger.d("请求参数: ${options.data}");
|
||
}
|
||
|
||
if (SPUtil.getString('token').isNotEmpty) {
|
||
options.headers['satoken'] = SPUtil.getString('token');
|
||
}
|
||
return handler.next(options);
|
||
},
|
||
),
|
||
);
|
||
|
||
// 添加响应拦截器
|
||
_dio.interceptors.add(
|
||
InterceptorsWrapper(
|
||
onResponse: (response, handler) {
|
||
logger.d("响应状态码: ${response.statusCode}");
|
||
// logger.d("响应数据: ${response.data}");
|
||
return handler.next(response);
|
||
},
|
||
),
|
||
);
|
||
|
||
// 添加错误拦截器
|
||
_dio.interceptors.add(
|
||
InterceptorsWrapper(
|
||
onError: (DioException e, handler) {
|
||
_handleError(e);
|
||
return handler.next(e);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
// 基础请求方法(处理所有类型的请求)
|
||
Future<T> _request<T>(
|
||
String path, {
|
||
required String method,
|
||
dynamic data,
|
||
Map<String, dynamic>? queryParameters,
|
||
T Function(dynamic data)? converter,
|
||
}) async {
|
||
try {
|
||
Response response = await _dio.request(
|
||
path,
|
||
data: data,
|
||
queryParameters: queryParameters,
|
||
options: Options(method: method),
|
||
);
|
||
|
||
// 处理响应数据
|
||
if (converter != null) {
|
||
return converter(response.data);
|
||
}
|
||
|
||
// 没有转换器时尝试直接返回(可能不安全,建议提供转换器)
|
||
return response.data;
|
||
} catch (e) {
|
||
throw Exception(_handleError(e));
|
||
}
|
||
}
|
||
|
||
// GET请求
|
||
Future<T> get<T>(
|
||
String path, {
|
||
Map<String, dynamic>? queryParameters,
|
||
T Function(dynamic data)? converter,
|
||
}) =>
|
||
_request(
|
||
path,
|
||
method: "GET",
|
||
queryParameters: queryParameters,
|
||
converter: converter,
|
||
);
|
||
|
||
// POST请求
|
||
Future<T> post<T>(
|
||
String path, {
|
||
dynamic data,
|
||
Map<String, dynamic>? queryParameters,
|
||
T Function(dynamic data)? converter,
|
||
}) =>
|
||
_request(
|
||
path,
|
||
method: "POST",
|
||
data: data,
|
||
queryParameters: queryParameters,
|
||
converter: converter,
|
||
);
|
||
|
||
// PUT请求
|
||
Future<T> put<T>(
|
||
String path, {
|
||
dynamic data,
|
||
Map<String, dynamic>? queryParameters,
|
||
T Function(dynamic data)? converter,
|
||
}) =>
|
||
_request(
|
||
path,
|
||
method: "PUT",
|
||
data: data,
|
||
queryParameters: queryParameters,
|
||
converter: converter,
|
||
);
|
||
|
||
// DELETE请求
|
||
Future<T> delete<T>(
|
||
String path, {
|
||
dynamic data,
|
||
Map<String, dynamic>? queryParameters,
|
||
T Function(dynamic data)? converter,
|
||
}) =>
|
||
_request(
|
||
path,
|
||
method: "DELETE",
|
||
data: data,
|
||
queryParameters: queryParameters,
|
||
converter: converter,
|
||
);
|
||
|
||
// 错误处理
|
||
String _handleError(dynamic error) {
|
||
String errorMessage = '未知错误';
|
||
if (error is DioException) {
|
||
switch (error.type) {
|
||
case DioExceptionType.connectionTimeout:
|
||
errorMessage = '连接超时,请检查网络连接';
|
||
break;
|
||
case DioExceptionType.sendTimeout:
|
||
errorMessage = '发送超时,请检查网络连接';
|
||
break;
|
||
case DioExceptionType.receiveTimeout:
|
||
errorMessage = '接收超时,请检查网络连接';
|
||
break;
|
||
case DioExceptionType.cancel:
|
||
errorMessage = '请求已取消';
|
||
break;
|
||
case DioExceptionType.badCertificate:
|
||
errorMessage = '证书验证失败';
|
||
break;
|
||
case DioExceptionType.badResponse:
|
||
// 处理HTTP响应错误(4xx, 5xx)
|
||
final statusCode = error.response?.statusCode ?? 0;
|
||
final responseData = error.response?.data;
|
||
|
||
if (responseData is Map<String, dynamic> &&
|
||
responseData.containsKey('message')) {
|
||
// 服务器返回了自定义错误消息
|
||
errorMessage = responseData['message']?.toString() ?? '未知错误';
|
||
} else {
|
||
errorMessage = _getHttpErrorMessage(statusCode);
|
||
}
|
||
break;
|
||
case DioExceptionType.connectionError:
|
||
errorMessage = '网络连接错误,请检查网络设置';
|
||
break;
|
||
case DioExceptionType.unknown:
|
||
if (error.error != null) {
|
||
errorMessage = '未知错误: ${error.error.toString()}';
|
||
}
|
||
errorMessage = '发生未知错误';
|
||
break;
|
||
}
|
||
} else {
|
||
errorMessage = '非Dio错误: $error';
|
||
}
|
||
|
||
logger.e(errorMessage);
|
||
return errorMessage;
|
||
}
|
||
|
||
String _getHttpErrorMessage(int statusCode) {
|
||
// 根据HTTP状态码返回对应的错误消息
|
||
switch (statusCode) {
|
||
case 400:
|
||
return '错误请求,请检查参数';
|
||
case 401:
|
||
return '未授权,请登录';
|
||
case 403:
|
||
return '禁止访问,权限不足';
|
||
case 404:
|
||
return '资源不存在';
|
||
case 405:
|
||
return '方法不允许';
|
||
case 408:
|
||
return '请求超时';
|
||
case 500:
|
||
return '服务器内部错误';
|
||
case 502:
|
||
return '网关错误';
|
||
case 503:
|
||
return '服务不可用';
|
||
case 504:
|
||
return '网关超时';
|
||
default:
|
||
return 'HTTP错误: $statusCode';
|
||
}
|
||
}
|
||
}
|