flutter_integrated_whb/lib/http/HttpManager.dart

173 lines
4.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:io';
import 'dart:ui';
import 'package:dio/dio.dart';
/// 全局接口异常
class ApiException implements Exception {
final String result;
final String message;
ApiException(this.result, this.message);
@override
String toString() => 'ApiException($result): $message';
}
/// HTTP 方法枚举
enum Method { get, post, put, delete }
/// HTTP 管理器 单例
class HttpManager {
HttpManager._internal() {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(milliseconds: 10000),
receiveTimeout: const Duration(milliseconds: 10000),
headers: {
'Content-Type': Headers.formUrlEncodedContentType,
},
));
_initInterceptors();
}
static final HttpManager _instance = HttpManager._internal();
factory HttpManager() => _instance;
late final Dio _dio;
// 添加401处理回调
static VoidCallback? onUnauthorized;
void _initInterceptors() {
_dio.interceptors
..add(LogInterceptor(request: true, responseBody: true, error: true))
..add(InterceptorsWrapper(onError: (err, handler) {
// 捕获401错误
if (err.response?.statusCode == 401) {
// 触发全局登出回调
onUnauthorized?.call();
// 创建自定义异常
final apiException = ApiException(
'提示',
'您的账号已在其他设备登录,已自动下线'
);
// 直接抛出业务异常,跳过后续错误处理
return handler.reject(
DioException(
requestOptions: err.requestOptions,
error: apiException,
response: err.response,
type: DioExceptionType.badResponse,
),
);
}
handler.next(err);
}));
}
/// 通用请求方法,返回完整后台 JSON
Future<Map<String, dynamic>> request(
String baseUrl,
String path, {
Method method = Method.post,
Map<String, dynamic>? data,
Map<String, dynamic>? params,
CancelToken? cancelToken,
}) async {
Response resp;
final url = baseUrl + path;
final options = Options(
method: method.name.toUpperCase(),
contentType: Headers.formUrlEncodedContentType,
);
try {
switch (method) {
case Method.get:
resp = await _dio.get(
url,
queryParameters: params,
cancelToken: cancelToken,
options: options,
);
break;
case Method.put:
resp = await _dio.put(
url,
data: data,
queryParameters: params,
cancelToken: cancelToken,
options: options,
);
break;
case Method.delete:
resp = await _dio.delete(
url,
queryParameters: params,
cancelToken: cancelToken,
options: options,
);
break;
case Method.post:
resp = await _dio.post(
url,
data: data,
queryParameters: params,
cancelToken: cancelToken,
options: options,
);
}
} on DioException catch (e) {
// 如果已经是ApiException类型401转换的
if (e.error is ApiException) {
throw e.error as ApiException;
}
// 其他网络错误
throw ApiException('network_error', e.message ?? e.toString());
}
// 解析返回 JSON
final json = resp.data is Map<String, dynamic>
? resp.data as Map<String, dynamic>
: <String, dynamic>{};
final result = json['result'] as String?;
final msg = json['msg'] as String? ?? json['message'] as String? ?? '';
if (result != 'success') {
// 非 success 都抛异常
throw ApiException(result ?? 'unknown', msg);
}
return json;
}
}
/// 上传图片扩展
extension HttpManagerUpload on HttpManager {
Future<Map<String, dynamic>> uploadFaceImage({
required String baseUrl,
required String path,
required Map<String, dynamic> fromData,
CancelToken? cancelToken,
}) async {
final form = FormData.fromMap(fromData);
try {
final resp = await _dio.post(
baseUrl + path,
data: form,
cancelToken: cancelToken,
options: Options(
method: Method.post.name.toUpperCase(),
contentType: 'multipart/form-data',
),
);
final json = resp.data is Map<String, dynamic>
? resp.data as Map<String, dynamic>
: <String, dynamic>{};
return json;
} on DioException catch (e) {
// 如果已经是ApiException类型401转换的
if (e.error is ApiException) {
throw e.error as ApiException;
}
throw ApiException('network_error', e.message ?? e.toString());
}
}
}