简介:
在移动应用程序开发中,优雅地处理错误对于提供流畅且用户友好的体验至关重要。使用 Flutter 时,Dio 这个 HTTP 请求库提供了有效的机制来处理网络请求期间的错误。在本文中,我们将探讨如何使用 Dio 有效处理 Flutter 中的错误,确保您的应用程序保持可靠且用户友好。
- Dio 简介:
Dio 是一个可以简化 Flutter 应用程序中的 HTTP 请求。它提供取消、拦截器、请求/响应转换和错误处理等功能,类似前端开发中的Axios请求库。错误处理是网络通信的一个重要方面,因为它允许应用程序处理无互联网连接、服务器错误等情况。 - Dio 配置和实例创建:
在我们深入讨论错误处理之前,让我们通过创建具有适当配置的实例来设置 Dio。我们将根据需求定义超时、基本 URL 和其他设置。使用拦截器进行全局错误处理:
const String APPLICATION_JSON = "application/json";
const String CONTENT_TYPE = "content-type";
const String ACCEPT = "accept";
const String AUTHORIZATION = "authorization";
const String DEFAULT_LANGUAGE = "en";
const String TOKEN = "token";
const String BASE_URL = "https://api.example.com";
class DioFactory {
Future<Dio> getDio() async {
Dio dio = Dio();
Map<String, String> headers = {
CONTENT_TYPE: APPLICATION_JSON,
ACCEPT: APPLICATION_JSON,
AUTHORIZATION: TOKEN,
DEFAULT_LANGUAGE: DEFAULT_LANGUAGE
};
dio.options = BaseOptions(
baseUrl: BASE_URL,
headers: headers,
receiveTimeout: Constants.apiTimeOut,
sendTimeout: Constants.apiTimeOut,
);
if (!kReleaseMode) {
dio.interceptors.add(PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseHeader: true,
));
}
return dio;
}
}
- DataSource 枚举:
这是一个定义各种数据源的枚举,每个数据源都与特定类型的故障相关。它用于将错误类型映射到失败响应。
enum DataSource {
SUCCESS,
NO_CONTENT,
BAD_REQUEST,
FORBIDDEN,
UNAUTORISED,
NOT_FOUND,
INTERNAL_SERVER_ERROR,
CONNECT_TIMEOUT,
CANCEL,
RECIEVE_TIMEOUT,
SEND_TIMEOUT,
CACHE_ERROR,
NO_INTERNET_CONNECTION,
DEFAULT
}
- DataSourceExtension:
此扩展向 DataSource 枚举添加了一个名为 getFailure 的方法。此方法根据枚举值返回一个 Failure 对象。
extension DataSourceExtension on DataSource {
Failure getFailure() {
var mContext = navigatorKey!.currentState!.context;
switch (this) {
case DataSource.SUCCESS:
return Failure(ResponseCode.SUCCESS, ResponseMessage.SUCCESS.tr(mContext));
case DataSource.NO_CONTENT:
return Failure(ResponseCode.NO_CONTENT, ResponseMessage.NO_CONTENT.tr(mContext));
case DataSource.BAD_REQUEST:
return Failure(ResponseCode.BAD_REQUEST, ResponseMessage.BAD_REQUEST.tr(mContext));
case DataSource.FORBIDDEN:
return Failure(ResponseCode.FORBIDDEN, ResponseMessage.FORBIDDEN.tr(mContext));
case DataSource.UNAUTORISED:
return Failure(ResponseCode.UNAUTORISED, ResponseMessage.UNAUTORISED.tr(mContext));
case DataSource.NOT_FOUND:
return Failure(ResponseCode.NOT_FOUND, ResponseMessage.NOT_FOUND.tr(mContext));
case DataSource.INTERNAL_SERVER_ERROR:
return Failure(ResponseCode.INTERNAL_SERVER_ERROR,
ResponseMessage.INTERNAL_SERVER_ERROR.tr(mContext));
case DataSource.CONNECT_TIMEOUT:
return Failure(
ResponseCode.CONNECT_TIMEOUT, ResponseMessage.CONNECT_TIMEOUT.tr(mContext));
case DataSource.CANCEL:
return Failure(ResponseCode.CANCEL, ResponseMessage.CANCEL.tr(mContext));
case DataSource.RECIEVE_TIMEOUT:
return Failure(
ResponseCode.RECIEVE_TIMEOUT, ResponseMessage.RECIEVE_TIMEOUT.tr(mContext));
case DataSource.SEND_TIMEOUT:
return Failure(ResponseCode.SEND_TIMEOUT, ResponseMessage.SEND_TIMEOUT.tr(mContext));
case DataSource.CACHE_ERROR:
return Failure(ResponseCode.CACHE_ERROR, ResponseMessage.CACHE_ERROR.tr(mContext));
case DataSource.NO_INTERNET_CONNECTION:
return Failure(ResponseCode.NO_INTERNET_CONNECTION,
ResponseMessage.NO_INTERNET_CONNECTION.tr(mContext));
case DataSource.DEFAULT:
return Failure(ResponseCode.DEFAULT, ResponseMessage.DEFAULT.tr(mContext));
}
}
}
- ResponseCode 类:
该类定义了表示各种 HTTP 状态码的静态整型常量,包括标准 HTTP 状态码和本地状态码的自定义状态码。
class ResponseCode {
static const int SUCCESS = 200; // success with data
static const int NO_CONTENT = 201; // success with no data (no content)
static const int BAD_REQUEST = 400; // failure, API rejected request
static const int UNAUTORISED = 401; // failure, user is not authorised
static const int FORBIDDEN = 403; // failure, API rejected request
static const int INTERNAL_SERVER_ERROR = 500; // failure, crash in server side
static const int NOT_FOUND = 404; // failure, not found
// local status code
static const int CONNECT_TIMEOUT = -1;
static const int CANCEL = -2;
static const int RECIEVE_TIMEOUT = -3;
static const int SEND_TIMEOUT = -4;
static const int CACHE_ERROR = -5;
static const int NO_INTERNET_CONNECTION = -6;
static const int DEFAULT = -7;
}
- ResponseMessage类:
该类定义了表示不同HTTP状态码的响应消息的静态字符串常量。这些消息将被国际化(使用本地化)。
class ResponseMessage {
static const String SUCCESS = AppStrings.success; // success with data
static const String NO_CONTENT = AppStrings.success; // success with no data (no content)
static const String BAD_REQUEST = AppStrings.strBadRequestError; // failure, API rejected request
static const String UNAUTORISED = AppStrings.strUnauthorizedError; // failure, user is not authorised
static const String FORBIDDEN = AppStrings.strForbiddenError; // failure, API rejected request
static const String INTERNAL_SERVER_ERROR = AppStrings.strInternalServerError; // failure, crash in server side
static const String NOT_FOUND = AppStrings.strNotFoundError; // failure, crash in server side
// local status code
static const String CONNECT_TIMEOUT = AppStrings.strTimeoutError;
static const String CANCEL = AppStrings.strDefaultError;
static const String RECIEVE_TIMEOUT = AppStrings.strTimeoutError;
static const String SEND_TIMEOUT = AppStrings.strTimeoutError;
static const String CACHE_ERROR = AppStrings.strCacheError;
static const String NO_INTERNET_CONNECTION = AppStrings.strNoInternetError;
static const String DEFAULT = AppStrings.strDefaultError;
}
- HandleError函数:
该私有函数以DioException为参数,返回一个Failure对象。它切换DioException的类型,并根据DataSource枚举中定义的一组枚举值将不同类型的DioException映射到相应的Failure值。
Failure _handleError(DioException error) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
return DataSource.CONNECT_TIMEOUT.getFailure();
case DioExceptionType.sendTimeout:
return DataSource.SEND_TIMEOUT.getFailure();
case DioExceptionType.receiveTimeout:
return DataSource.RECIEVE_TIMEOUT.getFailure();
case DioExceptionType.badResponse:
if (error.response != null &&
error.response?.statusCode != null &&
error.response?.statusMessage != null) {
return Failure(error.response?.statusCode ?? 0,
error.response?.statusMessage ?? "");
} else {
return DataSource.DEFAULT.getFailure();
}
case DioExceptionType.cancel:
return DataSource.CANCEL.getFailure();
default:
return DataSource.DEFAULT.getFailure();
}
}
8.ErrorHandler类:
该类实现了Exception接口,表明它用于处理异常。
它有一个名为 failure、类型为 Failure 的 Late 字段,该字段不会立即初始化。
ErrorHandler 类有一个名为handle 的构造函数,它采用动态错误参数。它根据错误的类型调用_handleError函数来处理不同类型的异常。
如果错误类型为 DioException,则调用 _handleError 函数来确定失败。
如果错误不是 DioException,则会将失败设置为从称为 DataSource 的数据源获取的默认值。
class ErrorHandler implements Exception {
late Failure failure;
ErrorHandler.handle(dynamic error) {
if (error is DioException) {
// dio error so its an error from response of the API or from dio itself
failure = _handleError(error);
} else {
// default error
failure = DataSource.DEFAULT.getFailure();
}
}
}
- 处理特定请求中的错误:
虽然全局错误处理很重要,但您也可以基于每个请求处理错误。使用try-catch来捕获错误并做出相应的响应。
Future<Either<Failure, ResponseDto>> getResponse(RequestDto requestDto) async {
if (await _networkInfo.isConnected) {
try {
...
.
.
return Right(response);
} catch (error) {
return Left(ErrorHandler.handle(error).failure);
}
} else {
return Left(DataSource.NO_INTERNET_CONNECTION.getFailure());
}
}
- 显示用户友好的错误消息:
为了确保良好的用户体验,请将技术错误消息转换为用户友好的消息。您可以使用辅助函数将错误代码映射到人类可读的消息,以指导用户如何继续操作。
"success": "成功",
"bad_request_error": "请求错误,请重试",
"no_content": "请求成功并且服务器创建了新的资源",
"forbidden_error": "请求已拒绝,请重试",
"unauthorized_error": "暂无权限,请重试",
"not_found_error": "请求地址不正确,请重试",
"conflict_error": "请求冲突,请重试",
"internal_server_error": "服务器端崩溃,请重试",
"unknown_error": "未知错误,请重试",
"timeout_error": "请求超时,请重试",
"default_error": "出了点问题,请稍后再试,请重试",
"cache_error": "缓存错误,请重试",
"no_internet_error": "请检查您的网络链接"
- 结论:
有效的错误处理对于提供健壮可靠的 Flutter 应用程序至关重要。Dio 全面的错误处理机制与用户友好的错误消息相结合,确保用户即使在较差的网络中也能随时了解情况。通过实施这些策略,可以提高应用程序的可靠性并增强整体用户体验。