Dio网络请求框架之CancelToken、DioErrorType、_DioErrorTypeExtension、DioError源码分析(二)

1,074 阅读5分钟

CancelToken

class CancelToken {
  CancelToken();

  final Completer<DioError> _completer = Completer<DioError>();

  /// Whether the [error] is thrown by [cancel].
  static bool isCancel(DioError error) => error.type == DioErrorType.cancel;

  /// If request have been canceled, save the cancel error.
  DioError? get cancelError => _cancelError;
  DioError? _cancelError;

  RequestOptions? requestOptions;

  /// Whether the token is cancelled.
  bool get isCancelled => _cancelError != null;

  /// When cancelled, this future will be resolved.
  Future<DioError> get whenCancel => _completer.future;

  /// Cancel the request with the given [reason].
  void cancel([Object? reason]) {
    _cancelError = DioError.requestCancelled(
      requestOptions: requestOptions ?? RequestOptions(),
      reason: reason,
      stackTrace: StackTrace.current,
    );
    if (!_completer.isCompleted) {
      _completer.complete(_cancelError);
    }
  }
}

CancelToken 是一个用于取消 Dio 请求的实例,基于 Completer 构建而成。

通过使用 CancelToken,你可以取消使用同一个 token 的多个请求。当调用 cancel 方法时,所有使用该 token 的请求都将被取消。

CancelToken 提供了一种机制,使你可以在需要取消请求的时候进行操作。例如,当用户手动取消请求或在某些特定条件下取消请求时,你可以使用 CancelToken 来取消请求并进行相应的处理。

构造函数 CancelToken() 创建一个 CancelToken 实例。

属性 _completer 是一个 Completer<DioError>,用于保存取消请求时产生的错误。

静态方法 isCancel 判断给定的 DioError 是否由 cancel 方法引起的取消。

属性 cancelError 保存取消请求时产生的 DioError

属性 requestOptions 保存与该取消令牌相关的请求选项。

属性 isCancelled 表示该取消令牌是否已取消。

属性 whenCancel 是一个 Future,在取消请求时将被解析。

方法 cancel 用于取消请求,并传入可选的 reason 参数。它会创建一个 DioError 对象,标记请求已被取消,并将其传递给 _completer 完成。

通过使用 CancelToken,你可以在需要取消请求的时候调用 cancel 方法,并通过监听 whenCancel Future 来处理取消请求的结果。

DioErrorType

enum DioErrorType {
  /// Caused by a connection timeout.
  connectionTimeout,

  /// It occurs when url is sent timeout.
  sendTimeout,

  ///It occurs when receiving timeout.
  receiveTimeout,

  /// Caused by an incorrect certificate as configured by [ValidateCertificate].
  badCertificate,

  /// The [DioError] was caused by an incorrect status code as configured by
  /// [ValidateStatus].
  badResponse,

  /// When the request is cancelled, dio will throw a error with this type.
  cancel,

  /// Caused for example by a `xhr.onError` or SocketExceptions.
  connectionError,

  /// Default error type, Some other [Error]. In this case, you can use the
  /// [DioError.error] if it is not null.
  unknown,
}

DioErrorType 是一个枚举类型,用于表示 Dio 请求时可能发生的错误类型。

  • connectionTimeout:连接超时错误,表示连接到服务器的时间超过了预设的超时时间。
  • sendTimeout:发送超时错误,表示发送请求数据的时间超过了预设的超时时间。
  • receiveTimeout:接收超时错误,表示接收服务器响应数据的时间超过了预设的超时时间。
  • badCertificate:无效证书错误,表示由于配置的证书验证器 [ValidateCertificate] 检测到证书不正确而引发的错误。
  • badResponse:无效响应错误,表示由于配置的状态码验证器 [ValidateStatus] 检测到响应状态码不正确而引发的错误。
  • cancel:取消请求错误,表示请求被取消引发的错误。
  • connectionError:连接错误,表示由于 xhr.onError 或 Socket 异常等引发的错误。
  • unknown:未知错误,默认的错误类型,表示其他类型的错误。在这种情况下,你可以使用 [DioError.error] 属性来获取具体的错误信息。

_DioErrorTypeExtension

extension _DioErrorTypeExtension on DioErrorType {
  String toPrettyDescription() {
    switch (this) {
      case DioErrorType.connectionTimeout:
        return 'connection timeout';
      case DioErrorType.sendTimeout:
        return 'send timeout';
      case DioErrorType.receiveTimeout:
        return 'receive timeout';
      case DioErrorType.badCertificate:
        return 'bad certificate';
      case DioErrorType.badResponse:
        return 'bad response';
      case DioErrorType.cancel:
        return 'request cancelled';
      case DioErrorType.connectionError:
        return 'connection error';
      case DioErrorType.unknown:
        return 'unknown';
    }
  }
}

该扩展 _DioErrorTypeExtensionDioErrorType 枚举类型添加了一个方法 toPrettyDescription(),该方法用于将 DioErrorType 转换为更友好的描述字符串。

根据不同的 DioErrorType 值,toPrettyDescription() 方法返回相应的描述字符串,如:

  • connectionTimeout 返回 'connection timeout',表示连接超时错误。
  • sendTimeout 返回 'send timeout',表示发送超时错误。
  • receiveTimeout 返回 'receive timeout',表示接收超时错误。
  • badCertificate 返回 'bad certificate',表示无效证书错误。
  • badResponse 返回 'bad response',表示无效响应错误。
  • cancel 返回 'request cancelled',表示请求被取消。
  • connectionError 返回 'connection error',表示连接错误。
  • unknown 返回 'unknown',表示未知错误。

通过使用这个扩展方法,你可以将 DioErrorType 转换为更具可读性的描述字符串,便于错误处理和展示。

DioError

/// [DioError] describes the exception info when a request failed.
class DioError implements Exception {
  /// Prefer using one of the other constructors.
  /// They're most likely better fitting.
  DioError({
    required this.requestOptions,
    this.response,
    this.type = DioErrorType.unknown,
    this.error,
    StackTrace? stackTrace,
    this.message,
  }) : stackTrace = identical(stackTrace, StackTrace.empty)
            ? requestOptions.sourceStackTrace ?? StackTrace.current
            : stackTrace ??
                requestOptions.sourceStackTrace ??
                StackTrace.current;

  factory DioError.badResponse({
    required int statusCode,
    required RequestOptions requestOptions,
    required Response response,
  }) =>
      DioError(
        type: DioErrorType.badResponse,
        message: 'The request returned an '
            'invalid status code of $statusCode.',
        requestOptions: requestOptions,
        response: response,
        error: null,
      );

  factory DioError.connectionTimeout({
    required Duration timeout,
    required RequestOptions requestOptions,
    Object? error,
  }) =>
      DioError(
        type: DioErrorType.connectionTimeout,
        message: 'The request connection took '
            'longer than $timeout. It was aborted.',
        requestOptions: requestOptions,
        response: null,
        error: error,
      );

  factory DioError.sendTimeout({
    required Duration timeout,
    required RequestOptions requestOptions,
  }) =>
      DioError(
        type: DioErrorType.sendTimeout,
        message: 'The request took '
            'longer than $timeout to send data. It was aborted.',
        requestOptions: requestOptions,
        response: null,
        error: null,
      );

  factory DioError.receiveTimeout({
    required Duration timeout,
    required RequestOptions requestOptions,
    Object? error,
  }) =>
      DioError(
        type: DioErrorType.receiveTimeout,
        message: 'The request took '
            'longer than $timeout to receive data. It was aborted.',
        requestOptions: requestOptions,
        response: null,
        error: error,
      );

  factory DioError.requestCancelled({
    required RequestOptions requestOptions,
    required Object? reason,
    StackTrace? stackTrace,
  }) =>
      DioError(
        type: DioErrorType.cancel,
        message: 'The request was cancelled.',
        requestOptions: requestOptions,
        response: null,
        error: reason,
        stackTrace: stackTrace,
      );

  factory DioError.connectionError({
    required RequestOptions requestOptions,
    required String reason,
  }) =>
      DioError(
        type: DioErrorType.connectionError,
        message: 'The connection errored: $reason',
        requestOptions: requestOptions,
        response: null,
        error: null,
      );

  /// The request info for the request that throws exception.
  final RequestOptions requestOptions;

  /// Response info, it may be `null` if the request can't reach to the
  /// HTTP server, for example, occurring a DNS error, network is not available.
  final Response? response;

  final DioErrorType type;

  /// The original error/exception object;
  /// It's usually not null when `type` is [DioErrorType.unknown].
  final Object? error;

  /// The stacktrace of the original error/exception object;
  /// It's usually not null when `type` is [DioErrorType.unknown].
  final StackTrace stackTrace;

  /// The error message that throws a [DioError].
  final String? message;

  /// Generate a new [DioError] by combining given values and original values.
  DioError copyWith({
    RequestOptions? requestOptions,
    Response? response,
    DioErrorType? type,
    Object? error,
    StackTrace? stackTrace,
    String? message,
  }) {
    return DioError(
      requestOptions: requestOptions ?? this.requestOptions,
      response: response ?? this.response,
      type: type ?? this.type,
      error: error ?? this.error,
      stackTrace: stackTrace ?? this.stackTrace,
      message: message ?? this.message,
    );
  }

  @override
  String toString() {
    String msg = 'DioError [${type.toPrettyDescription()}]: $message';
    if (error != null) {
      msg += '\nError: $error';
    }
    return msg;
  }
}

DioError 类用于描述请求失败时的异常信息。

构造函数:

  • DioError:创建一个 DioError 实例。

    • requestOptions:请求选项。
    • response:响应信息,如果请求无法到达 HTTP 服务器,例如发生 DNS 错误或网络不可用,则可能为 null
    • typeDioErrorType,默认为 DioErrorType.unknown,表示未知错误类型。
    • error:原始错误/异常对象,当 typeDioErrorType.unknown 时通常不为 null
    • stackTrace:原始错误/异常对象的堆栈跟踪,当 typeDioErrorType.unknown 时通常不为 null
    • message:抛出 DioError 的错误消息。

工厂构造函数:

  • DioError.badResponse:创建一个 DioError 实例,表示请求返回了无效的状态码。
  • DioError.connectionTimeout:创建一个 DioError 实例,表示请求连接超时。
  • DioError.sendTimeout:创建一个 DioError 实例,表示请求发送数据超时。
  • DioError.receiveTimeout:创建一个 DioError 实例,表示请求接收数据超时。
  • DioError.requestCancelled:创建一个 DioError 实例,表示请求被取消。
  • DioError.connectionError:创建一个 DioError 实例,表示连接错误。

属性:

  • requestOptions:引发异常的请求信息。
  • response:响应信息,可能为 null
  • typeDioErrorType 枚举类型,表示错误类型。
  • error:原始错误/异常对象,当 typeDioErrorType.unknown 时通常不为 null
  • stackTrace:原始错误/异常对象的堆栈跟踪,当 typeDioErrorType.unknown 时通常不为 null
  • message:引发 DioError 的错误消息。

方法:

  • copyWith:生成一个新的 DioError 实例,通过组合给定的值和原始值。
  • toString:返回 DioError 的字符串表示形式,包括错误类型和错误消息。

DioError 类用于描述 Dio 请求失败时的异常信息,其中包括请求选项、响应信息、错误类型、原始错误对象等。通过使用该类,可以更好地捕获和处理 Dio 请求过程中可能发生的异常情况。