90 lines
2.6 KiB
Dart
90 lines
2.6 KiB
Dart
import 'package:dio/dio.dart';
|
|
|
|
abstract class ApiException implements Exception {
|
|
final String message;
|
|
final int? statusCode;
|
|
|
|
const ApiException(this.message, {this.statusCode});
|
|
|
|
@override
|
|
String toString() => message;
|
|
|
|
factory ApiException.fromDioError(Object error) {
|
|
if (error is ApiException) return error;
|
|
if (error is DioException) {
|
|
final response = error.response;
|
|
final statusCode = response?.statusCode;
|
|
final data = response?.data;
|
|
|
|
String detail;
|
|
if (data is Map<String, dynamic>) {
|
|
detail =
|
|
(data['detail'] ?? data['message'] ?? data['error'])?.toString() ??
|
|
'请求失败';
|
|
} else {
|
|
detail = _networkErrorMessage(error);
|
|
}
|
|
|
|
final localized = _localizeError(detail, statusCode);
|
|
|
|
if (statusCode == 401) {
|
|
return UnauthorizedException(localized);
|
|
}
|
|
if (statusCode == 422) {
|
|
return ValidationException(
|
|
localized,
|
|
errors: data,
|
|
statusCode: statusCode,
|
|
);
|
|
}
|
|
return ServerException(localized, statusCode: statusCode);
|
|
}
|
|
return const ServerException('网络错误');
|
|
}
|
|
|
|
static String _localizeError(String detail, int? statusCode) {
|
|
if (statusCode == 403) {
|
|
return '没有权限执行此操作';
|
|
}
|
|
if (statusCode == 404) {
|
|
return '请求的资源不存在';
|
|
}
|
|
if (statusCode == 429) {
|
|
return '请求过于频繁,请稍后再试';
|
|
}
|
|
if (statusCode != null && statusCode >= 500) {
|
|
return '服务器错误,请稍后再试';
|
|
}
|
|
return detail;
|
|
}
|
|
|
|
static String _networkErrorMessage(DioException error) {
|
|
if (error.type == DioExceptionType.connectionTimeout ||
|
|
error.type == DioExceptionType.sendTimeout ||
|
|
error.type == DioExceptionType.receiveTimeout) {
|
|
return '网络超时,请确认手机与服务端在同一网络后重试';
|
|
}
|
|
|
|
if (error.type == DioExceptionType.connectionError ||
|
|
error.type == DioExceptionType.unknown) {
|
|
return '无法连接服务器。请在 iPhone 设置中为本应用开启“无线数据(WLAN与蜂窝网络)”,并确认本地网络权限已开启。';
|
|
}
|
|
|
|
return '请求失败';
|
|
}
|
|
}
|
|
|
|
class ServerException extends ApiException {
|
|
const ServerException(super.message, {super.statusCode});
|
|
}
|
|
|
|
class UnauthorizedException extends ApiException {
|
|
const UnauthorizedException([super.message = '请重新登录'])
|
|
: super(statusCode: 401);
|
|
}
|
|
|
|
class ValidationException extends ApiException {
|
|
final Map<String, dynamic>? errors;
|
|
const ValidationException(super.message, {this.errors, super.statusCode});
|
|
}
|