一、接上篇,实现Axios实现的网络封装。

昨天已经封装了BaseHttpClient,Axios只需要继承BaseHttpClient即可,代码实现如下:

import 'package:dio/dio.dart';
import 'package:flutter_axios/flutter_axios.dart' hide Interceptor, ProgressCallback;
import 'base_http_client.dart';
import 'config/api_config.dart';
import 'exceptions/app_exceptions.dart';
import 'models/api_response.dart';

class AxiosHttpClient extends BaseHttpClient {
  late AxiosInstance _api;
  String _baseUrl = ApiConfig.baseUrl;
  Map<String, String> _headers = ApiConfig.defaultHeaders;
  Duration _connectTimeout = ApiConfig.connectTimeout;
  Duration _receiveTimeout = ApiConfig.receiveTimeout;
  Duration _sendTimeout = ApiConfig.sendTimeout;

  AxiosHttpClient() {
    _initAxios();
  }

  void _initAxios() {
    _api = Axios.create(
      AxiosConfig(
        baseURL: _baseUrl,
        timeout: _connectTimeout,
        headers: _headers,
      ),
    );
  }

  @override
  void setBaseUrl(String baseUrl) {
    _baseUrl = baseUrl;
    _initAxios();
  }

  @override
  void setHeaders(Map<String, String> headers) {
    _headers = {..._headers, ...headers};
    _initAxios();
  }

  @override
  void setTimeout(Duration connectTimeout, Duration receiveTimeout, Duration sendTimeout) {
    _connectTimeout = connectTimeout;
    _receiveTimeout = receiveTimeout;
    _sendTimeout = sendTimeout;
    _initAxios();
  }

  @override
  void addInterceptor(Interceptor interceptor) {}

  @override
  void removeInterceptor(Interceptor interceptor) {}

  @override
  void clearInterceptors() {}

  @override
  Future<ApiResponse<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.get(_buildUrl(path, queryParameters)),
    );
  }

  @override
  Future<ApiResponse<T>> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.post(_buildUrl(path, queryParameters), data: data),
    );
  }

  @override
  Future<ApiResponse<T>> put<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.put(_buildUrl(path, queryParameters), data: data),
    );
  }

  @override
  Future<ApiResponse<T>> delete<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.delete(_buildUrl(path, queryParameters)),
    );
  }

  @override
  Future<ApiResponse<T>> patch<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.patch(_buildUrl(path, queryParameters), data: data),
    );
  }

  @override
  void cancelRequest(String requestId) {}

  @override
  void cancelAllRequests() {}

  @override
  Future<ApiResponse<T>> upload<T>(
    String path, {
    required String filePath,
    required String fileKey,
    Map<String, dynamic>? data,
    ProgressCallback? onSendProgress,
    Options? options,
    String? requestId,
  }) async {
    throw UnimplementedError();
  }

  @override
  Future<ApiResponse<T>> download<T>(
    String path, {
    required String savePath,
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? queryParameters,
    Options? options,
    String? requestId,
  }) async {
    return _executeRequest<T>(
      () async => await _api.get(_buildUrl(path, queryParameters)),
    );
  }

  @override
  void setAuthToken(String token) {
    _headers = {..._headers, 'Authorization': 'Bearer $token'};
    _initAxios();
  }

  @override
  void clearAuthToken() {
    _headers = Map.from(_headers)..remove('Authorization');
    _initAxios();
  }

  @override
  bool isNetworkError(DioException error) {
    return error.type == DioExceptionType.connectionTimeout ||
        error.type == DioExceptionType.sendTimeout ||
        error.type == DioExceptionType.receiveTimeout ||
        error.type == DioExceptionType.connectionError ||
        error.type == DioExceptionType.unknown;
  }

  @override
  bool isServerError(int statusCode) {
    return statusCode >= 500 && statusCode < 600;
  }

  @override
  void dispose() {}

  String _buildUrl(String path, Map<String, dynamic>? query) {
    if (query == null || query.isEmpty) return path;
    final uri = Uri(path: path, queryParameters: query.map((k, v) => MapEntry(k, v?.toString())));
    return uri.toString();
  }

  Future<ApiResponse<T>> _executeRequest<T>(
    Future<AxiosResponse> Function() request,
  ) async {
    try {
      final response = await request();
      final data = response.data as T;
      final headers = response.headers;
      final status = response.status;
      return ApiResponse.success(
        data,
        statusCode: status ?? 200,
        headers: headers,
      );
    } catch (e) {
      throw _handleAxiosError(e);
    }
  }

  AppException _handleAxiosError(Object error) {
    final msg = error.toString();
    if (msg.contains('Timeout')) {
      return TimeoutException('请求超时,请检查网络连接');
    }
    if (msg.contains('Network') || msg.contains('Socket')) {
      return NetworkException('网络连接失败,请检查网络设置');
    }
    if (msg.contains('401')) {
      return AuthenticationException('身份验证失败');
    }
    if (msg.contains('404')) {
      return ServerException('资源未找到', 404);
    }
    return UnknownException('未知错误: $msg');
  }
}

使用的时候只需要把HttpClient替换成AxiosHttpClient即可

效果如下:

关于网络请求的封装暂时告一个段落,还有一些问题暂时没时间解决,比如网络请求涉及到的Interceptor、Exception、Options等用的都是Dio库的类,自己并没有单独处理,这导致axios仅仅实现了HttpClient,并没有完全脱离Dio,有时间再优化。

二、代码上传到GitCode仓库

1、在 GitCode 上创建新仓库

地址:https://gitcode.com/create?position=nav_top

2、在本地项目初始化 Git(如果尚未初始化)

@@@ pocket % git init
Initialized empty Git repository in /Users/xxxxx/Desktop/flutter/pocket/.git/

3、将本地仓库与 GitCode 远程仓库关联

git remote add origin https://gitcode.com/xiaoyuxia/gitcode-pocket.git

4、添加文件、提交更改

git add .

5、推送到 GitCode

推送前拉一下仓库:

git pull origin main

commit:

git commit -m "initial commit"

push:

git push -u origin main:main

https://gitcode.com/xiaoyuxia/gitcode-pocket 如下:

😁,代码已上传到GitCode仓库,需要的可以点击此处下载

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐