fix:参考金值,研听,研值工程通用通用工程公共能力,重点对比网络请求、登录认证、状态管理、本地存储、WebView、推送、版本更新、一键登录、主题/UI 等模块,确定后续统一整改方案
This commit is contained in:
1 parent
6997a65c96
commit
d364c6d908
46 files changed
+2060
-395
No files matched your search
@@ -0,0 +1,123 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../models/pb/errors/errors.pb.dart' as kerrors;
|
||||
|
||||
enum ApiExceptionKind { transport, http, backend, decode, cancelled }
|
||||
|
||||
/// The single network failure shape exposed to application code.
|
||||
class ApiException implements Exception {
|
||||
const ApiException({
|
||||
required this.kind,
|
||||
this.httpStatus,
|
||||
this.backendCode,
|
||||
this.reason,
|
||||
this.serverMessage,
|
||||
this.requestOptions,
|
||||
this.cause,
|
||||
});
|
||||
|
||||
final ApiExceptionKind kind;
|
||||
final int? httpStatus;
|
||||
final int? backendCode;
|
||||
final String? reason;
|
||||
final String? serverMessage;
|
||||
final RequestOptions? requestOptions;
|
||||
final Object? cause;
|
||||
|
||||
bool get isUnauthorized =>
|
||||
httpStatus == 401 ||
|
||||
const {'INVALID_TOKEN', 'TOKEN_EXPIRED'}.contains(reason?.toUpperCase());
|
||||
|
||||
bool get isRetryable {
|
||||
if (kind == ApiExceptionKind.transport) return true;
|
||||
final status = httpStatus;
|
||||
return status == 408 || status == 429 || (status != null && status >= 500);
|
||||
}
|
||||
|
||||
int? get sessionGeneration =>
|
||||
requestOptions?.extra[authSessionGenerationExtra] as int?;
|
||||
|
||||
static ApiException from(Object error, {StackTrace? stackTrace}) {
|
||||
if (error is ApiException) return error;
|
||||
if (error is DioException) {
|
||||
if (error.error is ApiException) return error.error! as ApiException;
|
||||
return fromDio(error);
|
||||
}
|
||||
if (error is FormatException) {
|
||||
return ApiException(kind: ApiExceptionKind.decode, cause: error);
|
||||
}
|
||||
return ApiException(kind: ApiExceptionKind.decode, cause: error);
|
||||
}
|
||||
|
||||
static ApiException fromDio(DioException error) {
|
||||
final response = error.response;
|
||||
final status = response?.statusCode;
|
||||
final kratos = parseKratosError(response?.data);
|
||||
if (error.type == DioExceptionType.cancel) {
|
||||
return ApiException(
|
||||
kind: ApiExceptionKind.cancelled,
|
||||
httpStatus: status,
|
||||
requestOptions: error.requestOptions,
|
||||
cause: error,
|
||||
);
|
||||
}
|
||||
final kind = kratos != null
|
||||
? ApiExceptionKind.backend
|
||||
: status != null
|
||||
? ApiExceptionKind.http
|
||||
: ApiExceptionKind.transport;
|
||||
return ApiException(
|
||||
kind: kind,
|
||||
httpStatus: status,
|
||||
backendCode: kratos?.code,
|
||||
reason: _normalized(kratos?.reason),
|
||||
serverMessage: _normalized(kratos?.message),
|
||||
requestOptions: error.requestOptions,
|
||||
cause: error,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ApiException($kind, status: $httpStatus, code: $backendCode, '
|
||||
'reason: $reason, message: $serverMessage)';
|
||||
}
|
||||
|
||||
const authSessionGenerationExtra = 'authSessionGeneration';
|
||||
|
||||
kerrors.Status? parseKratosError(Object? raw) {
|
||||
if (raw is List<int> && raw.isNotEmpty) {
|
||||
try {
|
||||
final status = kerrors.Status.fromBuffer(raw);
|
||||
if (status.code != 0 || status.reason.isNotEmpty) return status;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (raw is Map) {
|
||||
final code = raw['code'];
|
||||
final reason = '${raw['reason'] ?? ''}';
|
||||
final message = '${raw['message'] ?? ''}';
|
||||
if (code is num || reason.isNotEmpty || message.isNotEmpty) {
|
||||
return kerrors.Status(
|
||||
code: code is num ? code.toInt() : 0,
|
||||
reason: reason,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? _normalized(String? value) {
|
||||
final result = value?.trim() ?? '';
|
||||
return result.isEmpty ? null : result;
|
||||
}
|
||||
|
||||
class ApiExceptionInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
final apiException = ApiException.fromDio(err);
|
||||
handler.next(err.copyWith(error: apiException));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user