402 lines
13 KiB
Dart
402 lines
13 KiB
Dart
import 'dart:io';
|
||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||
import 'package:dio/dio.dart';
|
||
import 'package:dio/adapter.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:fluttertoast/fluttertoast.dart';
|
||
import 'package:marking_app/common/config/request_config.dart';
|
||
import 'package:marking_app/routes/RouterManager.dart';
|
||
import 'package:marking_app/utils/index.dart';
|
||
import 'package:marking_app/utils/the_global.dart';
|
||
import 'package:package_info/package_info.dart';
|
||
|
||
class RestDio {
|
||
late Dio _dio;
|
||
// 单例模式
|
||
static final RestDio _instance = RestDio._internal();
|
||
factory RestDio() => _instance;
|
||
RestDio._internal() {
|
||
init();
|
||
}
|
||
|
||
// 初始化请求配置
|
||
init() {
|
||
BaseOptions options = BaseOptions(
|
||
connectTimeout: RequestConfig.connectTimeout,
|
||
receiveTimeout: RequestConfig.receiveTimeout,
|
||
);
|
||
_dio = Dio(options);
|
||
_dio.interceptors.add(AuthInterceptor()); // 添加 token
|
||
_dio.interceptors.add(ResponseHandle()); // 添加 数据返回拦截
|
||
_dio.interceptors.add(TheError()); // 添加 数据返回拦截
|
||
const isProd = bool.fromEnvironment('dart.vm.product');
|
||
if (!isProd && RequestConfig.requestDataPrinting) {
|
||
_dio.interceptors.add(LogInterceptor(responseBody: true, requestBody: true)); //添加日志
|
||
}
|
||
// 添加https证书
|
||
setHttpsPEM();
|
||
// setHttpsPKCS12();
|
||
|
||
// 使用代理
|
||
// setFindProxy();
|
||
}
|
||
|
||
String PEM = "XXXXX"; // certificate content
|
||
String PKCS12File = "XXXXX"; // certificate content
|
||
|
||
//dio 基本方法
|
||
Future<Dio> getDio() async {
|
||
return _dio;
|
||
}
|
||
|
||
//添加证书
|
||
setHttpsPEM() async {
|
||
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
|
||
client.badCertificateCallback = (X509Certificate cert, String host, int port) {
|
||
// if (cert.pem == PEM) {
|
||
// // Verify the certificate
|
||
// return true;
|
||
// }
|
||
// return false;
|
||
return true;
|
||
};
|
||
};
|
||
}
|
||
|
||
setHttpsPKCS12() async {
|
||
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
|
||
SecurityContext sc = new SecurityContext();
|
||
//file is the path of certificate
|
||
sc.setTrustedCertificates(PKCS12File);
|
||
HttpClient httpClient = new HttpClient(context: sc);
|
||
return httpClient;
|
||
};
|
||
}
|
||
|
||
//设置代理
|
||
setFindProxy() async {
|
||
// 设置请求拦截器
|
||
_dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
|
||
// 判断 URI 的路径是否包含指定字符串
|
||
if (options.uri.path.contains(RequestConfig.hwProxyKeywords)) {
|
||
// 设置代理地址
|
||
_dio.httpClientAdapter = DefaultHttpClientAdapter();
|
||
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
|
||
client.findProxy = (uri) {
|
||
print('进入开始代理...............');
|
||
return RequestConfig.proBaseUrlOfHomework;
|
||
};
|
||
};
|
||
}
|
||
|
||
return handler.next(options); // 继续发送请求
|
||
}));
|
||
}
|
||
}
|
||
|
||
/*
|
||
*
|
||
*AuthInterceptor
|
||
*添加header认证
|
||
* */
|
||
class AuthInterceptor extends Interceptor {
|
||
String PLATFORM = "android"; //可根据代码进行判断
|
||
|
||
@override
|
||
onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
|
||
//获取app版本
|
||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||
|
||
String version = packageInfo.version;
|
||
if (Platform.isIOS) {
|
||
PLATFORM = "ios";
|
||
} else if (Platform.isAndroid) {
|
||
PLATFORM = "android";
|
||
} else if (Platform.isWindows) {
|
||
PLATFORM = "Windows";
|
||
} else if (Platform.isMacOS) {
|
||
PLATFORM = "macos";
|
||
} else if (Platform.isLinux) {
|
||
PLATFORM = "Linux";
|
||
}
|
||
|
||
Map<String, String> headers = {};
|
||
headers["Accept-Charset"] = "utf-8";
|
||
headers["Connection"] = "keep-alive";
|
||
headers["Accept"] = "*/*";
|
||
headers["x-version"] = version; //自己更改配置
|
||
headers["x-platform"] = PLATFORM;
|
||
//获取存储数据 保存header token
|
||
String? token = await FastData.getInstance().getToken();
|
||
|
||
if (null != token && token.isNotEmpty) {
|
||
headers["Authorization"] = 'Bearer $token'; //添加自己项目中的请求头 进行保存
|
||
}
|
||
|
||
options.headers = headers;
|
||
Uri uri = options.uri;
|
||
print(uri.toString());
|
||
// 判断请求的 URL 是否包含指定字符串
|
||
if (uri.toString().contains(RequestConfig.hwProxyKeywords)) {
|
||
String newPath = options.path.replaceFirst(RegExp('^${RequestConfig.hwProxyKeywords}'), '');
|
||
options.path = newPath;
|
||
// 修改请求的 URL
|
||
options.baseUrl = RequestConfig.proBaseUrlOfHomework;
|
||
}
|
||
|
||
// print('请求参数query:' + jsonEncode(options.queryParameters));
|
||
// print('请求参数data:' + jsonEncode(options.data));
|
||
|
||
return super.onRequest(options, handler);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ResponseHandle
|
||
* 监听返回响应
|
||
**/
|
||
class ResponseHandle extends Interceptor {
|
||
@override
|
||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||
const isProd = bool.fromEnvironment('dart.vm.product');
|
||
if (!isProd && RequestConfig.requestDataPrinting) {
|
||
// printJson(response.data);
|
||
}
|
||
super.onResponse(response, handler);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ResponseHandle
|
||
* 监听返回响应
|
||
**/
|
||
class TheError extends Interceptor {
|
||
// 是否有网
|
||
Future<bool> isConnected() async {
|
||
var connectivityResult = await (Connectivity().checkConnectivity());
|
||
return connectivityResult != ConnectivityResult.none;
|
||
}
|
||
|
||
@override
|
||
Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
|
||
// 自定义一个socket实例,因为dio原生的实例,message属于是只读的
|
||
// 这里是我单独加的,因为默认的dio err实例,的几种类型,缺少无网络情况下的错误提示信息
|
||
if (err.error is SocketException) {
|
||
err.error = MyDioSocketException(
|
||
err.message.contains('111') ? "服务器拒绝连接,请重试" : err.message,
|
||
osError: err.error?.osError,
|
||
address: err.error?.address,
|
||
port: err.error?.port,
|
||
);
|
||
}
|
||
// dio默认的错误实例,如果是没有网络,只能得到一个未知错误,无法精准的得知是否是无网络的情况
|
||
if (err.type == DioErrorType.other) {
|
||
bool isConnectNetWork = await isConnected();
|
||
if (!isConnectNetWork && err.error is MyDioSocketException) {
|
||
err.error.message = "当前网络不可用,请检查您的网络";
|
||
}
|
||
}
|
||
|
||
// error统一处理
|
||
AppException appException = AppException.create(err);
|
||
|
||
// 错误提示
|
||
// debugPrint('DioError===: ${appException.toString()}');
|
||
err = appException;
|
||
|
||
return super.onError(err, handler);
|
||
}
|
||
}
|
||
|
||
String getDioErrorTypeStr(DioError err) {
|
||
DioErrorType errorType = err.type;
|
||
String str;
|
||
switch (errorType) {
|
||
case DioErrorType.connectTimeout:
|
||
str = '连接超时,请检查网络再重试';
|
||
break;
|
||
case DioErrorType.sendTimeout:
|
||
str = '发送时间超时,请重试';
|
||
break;
|
||
case DioErrorType.receiveTimeout:
|
||
str = '接收数据超时,请重试';
|
||
break;
|
||
case DioErrorType.response:
|
||
str = '请求返回失败';
|
||
break;
|
||
case DioErrorType.cancel:
|
||
str = '请求取消';
|
||
break;
|
||
case DioErrorType.other:
|
||
str = '请求其他错误';
|
||
break;
|
||
}
|
||
return str;
|
||
}
|
||
|
||
/// 自定义异常
|
||
class AppException extends DioError {
|
||
final String _message;
|
||
final int _code;
|
||
AppException(this._code, this._message, StackTrace? theStackTrace,
|
||
{required super.requestOptions, super.type, super.error}) {
|
||
super.stackTrace = theStackTrace;
|
||
}
|
||
|
||
@override
|
||
String toString() {
|
||
return "$_code$_message" + super.toString();
|
||
}
|
||
|
||
String getMessage() {
|
||
return _message;
|
||
}
|
||
|
||
factory AppException.create(DioError error) {
|
||
switch (error.type) {
|
||
case DioErrorType.cancel:
|
||
{
|
||
return AppException(-1, "请求取消", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case DioErrorType.connectTimeout:
|
||
{
|
||
return AppException(-1, "连接超时", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case DioErrorType.sendTimeout:
|
||
{
|
||
return AppException(-1, "请求超时", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case DioErrorType.receiveTimeout:
|
||
{
|
||
return AppException(-1, "响应超时", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
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 AppException(errCode!, "请求语法错误", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 401:
|
||
{
|
||
var currentContext = TheGlobal.navigatorKey.currentState?.overlay?.context;
|
||
if (currentContext != null) {
|
||
var routePath = ModalRoute.of(currentContext)?.settings;
|
||
if (routePath != null) {
|
||
} else {
|
||
if (TheGlobal.navigatorKey.currentContext != null) {
|
||
ExceptionHandle.toLogin(TheGlobal.navigatorKey.currentContext);
|
||
}
|
||
}
|
||
}
|
||
return AppException(errCode!, "登录信息过期,请重新登录", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 403:
|
||
{
|
||
return AppException(errCode!, "服务器拒绝执行", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 404:
|
||
{
|
||
return AppException(errCode!, "无法连接服务器", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 405:
|
||
{
|
||
return AppException(errCode!, "请求方法被禁止", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 500:
|
||
{
|
||
return AppException(errCode!, "服务器内部错误", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 502:
|
||
{
|
||
return AppException(errCode!, "无效的请求", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 503:
|
||
{
|
||
return AppException(errCode!, "服务器挂了", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
case 505:
|
||
{
|
||
return AppException(errCode!, "不支持HTTP协议请求", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
default:
|
||
{
|
||
// return ErrorEntity(code: errCode, message: "未知错误");
|
||
return AppException(errCode!, error.response!.statusMessage!, error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
}
|
||
} on Exception catch (_) {
|
||
return AppException(-1, "未知错误", error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
}
|
||
default:
|
||
{
|
||
return AppException(-1, error.error.message, error.stackTrace,
|
||
requestOptions: error.requestOptions, type: error.type, error: error.error);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
class ExceptionHandle {
|
||
// 异常处理信息
|
||
static void exceptionPrompt(BuildContext context, AppException err, {bool logicHandle = true}) {
|
||
ToastUtils.getFluttertoast(
|
||
context: context,
|
||
msg: err._message,
|
||
backgroundColor: Colors.grey[350],
|
||
toastLength: Toast.LENGTH_LONG,
|
||
);
|
||
if (!logicHandle) return;
|
||
switch (err._code) {
|
||
case 401:
|
||
toLogin(context); // 重新前往登录
|
||
break;
|
||
default:
|
||
}
|
||
}
|
||
|
||
static void toLogin(context, {int timeer = 800}) {
|
||
setTimeOut(
|
||
timeer,
|
||
() => RouterManager.router.navigateTo(context, RouterManager.loginPath, clearStack: true),
|
||
);
|
||
}
|
||
}
|
||
|
||
// 这里是一个我单独写得soket错误实例,因为dio默认生成的是不允许修改message内容的,我只能自定义一个使用
|
||
class MyDioSocketException extends SocketException {
|
||
String message;
|
||
|
||
MyDioSocketException(
|
||
this.message, {
|
||
osError,
|
||
address,
|
||
port,
|
||
}) : super(
|
||
message,
|
||
osError: osError,
|
||
address: address,
|
||
port: port,
|
||
);
|
||
}
|