FlutterIM项目_Part2_网络工具类:网络工具类的封装及注意点

94 阅读4分钟

一、封装网络工具类的注意点

0.单例模式

工具类用的很频繁,所以不可能每次用到的地方都创造一个实例,那会浪费大量内存。全局共享一个单例就行了。

1.常用请求方法封装

get/post/put/delete/上传文件/下载文件等常用请求方法

2.全局配置:请求参数和头部处理

配置全局的headers

配置baseurl

headers需要动态可添加

3.拦截器(请求和响应拦截)

  • 请求拦截:在发送请求之前可以进行统一的处理,例如添加认证信息、日志记录、请求调试等。(在这里进行token的添加,和token刷新机制的处理)
  • 响应拦截:可以在收到响应后进行统一处理,例如统一错误处理、日志记录等。

4.响应处理

  • 响应解析:封装统一的响应格式,解析响应内容,如JSON、XML、文本等。
  • 状态码处理:针对不同的HTTP状态码(如200、400、404、500等)进行处理,提供友好的错误提示。
  • 超时处理:可以设置请求超时时间,并根据超时情况进行处理(例如重新请求、返回超时错误等)。

5.日志记录

  • 请求/响应日志:为开发者提供请求和响应的日志信息,帮助调试和分析。 (注意这里日志默认输出就是一行,如果内容太长,中间会被省略掉。所以需要自定义一下日志打印, 比如我要定义如下格式日志: 时间 日志级别 日志内容, 日志内容过长需要分成多段打印。)
  • 调试模式:在开发过程中,可以开启调试模式,输出详细的请求、响应数据等信息。

6.错误处理

二、代码如下

http_config.dart

enum Env {
  dev,
  pre,
  prd;

  static Env current = dev;
}

extension BaseUrl on Env {
  String get baseUrl {
    switch (this) {
      case Env.dev:
        return 'www.dev-env.com';
      case Env.pre:
        return 'www.pre-env.com';
      case Env.prd:
        return 'www.prd-env.com';
      default:
        return 'www.dev-env.com';
    }
  }
}

http_inteceptors.dart

import 'package:dio/dio.dart';

class RequestInterceptor extends Interceptor {
  Future<String> getToken() async {
    return 'token';
  }

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    super.onRequest(options, handler);
    //请求发送前添加逻辑
    options.headers['token'] = await getToken();

    //继续执行请求
    return handler.next(options);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // TODO: implement onError
    super.onError(err, handler);
    // 当访问令牌过期时,受保护的资源会返回401 Unauthorized错误。
    // 此时,客户端需要使用刷新令牌获取新的访问令牌。
    //TODO: token刷新机制
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // TODO: implement onResponse
    super.onResponse(response, handler);
  }
}

http_manager.dart

import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:http/src/http_interceptors.dart';
import 'http_config.dart';

class HttpManager {
  static String val = '';
  static late Dio _dio;

  //单例模式
  static late final HttpManager _httpManager = HttpManager._internal();
  factory HttpManager.shared() => _httpManager;
  HttpManager._internal() {
    _dio = Dio();
    //全局配置,请求参数和头部处理
    _dio.options = BaseOptions(
      baseUrl: Env.current.baseUrl,
      headers: {'app-name': 'test-app'},
      connectTimeout: const Duration(milliseconds: 5000),
      receiveTimeout: const Duration(milliseconds: 5000),
    );
    //拦截器(这里只进行请求拦截)
    _dio.interceptors.add(RequestInterceptor());
    //TODO: 这里加个日志拦截器专门对日志进行处理
    //TODO: 或者把token刷新的逻辑提取出来,专门的token刷新机制进行处理
    //但是要确定拦截器是并发的还是顺序执行的
  }
  Future<Map<String, dynamic>?> request(
    String path, {
    String? method,
    Object? data,
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
  }) async {
    try {
      Response response = await _dio.request(
        path,
        data: data,
        queryParameters: queryParameters,
        options: Options(
          method: method,
          headers: headers,
          extra: extra,
        ),
      );
      //TODO:日志记录
      //这里对响应进行处理
      return _handleResponse(response);
    } on DioException catch (e) {
      //异常处理
      return _handleException(e);
    }
  }

  Future<Map<String, dynamic>?> get(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
  }) async {
    Map<String, dynamic>? response = await request(
      path,
      data: data,
      queryParameters: queryParameters,
      method: 'GET',
      extra: extra,
      headers: headers,
    );
    return response;
  }

  Future<Map<String, dynamic>?> post(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
  }) async {
    Map<String, dynamic>? response = await request(
      path,
      data: data,
      queryParameters: queryParameters,
      method: 'POST',
      extra: extra,
      headers: headers,
    );
    return response;
  }

  Future<Map<String, dynamic>?> put(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
  }) async {
    Map<String, dynamic>? response = await request(
      path,
      data: data,
      queryParameters: queryParameters,
      method: 'PUT',
      extra: extra,
      headers: headers,
    );
    return response;
  }

  Future<Map<String, dynamic>?> delete(
    String path, {
    Object? data,
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
  }) async {
    Map<String, dynamic>? response = await request(
      path,
      data: data,
      queryParameters: queryParameters,
      method: 'DElETE',
      extra: extra,
      headers: headers,
    );
    return response;
  }

  ///处理请求结果
  //这一步可以在onResponse拦截进行预处理,看着办。
  Map<String, dynamic>? _handleResponse(Response response) {
    dynamic responseData;
    //用户只对data字段感兴趣
    if (response.data != null &&
        response.data is Map &&
        response.data['data'] != null) {
      responseData = response.data['data'];
      //如果这里有服务端自定义的错误码如response.data['code'];
      //可以依据code进行业务层的错误处理
    }
    return responseData;
  }

  ///处理异常
  Map<String, dynamic>? _handleException(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
        //连接超时处理
        break;
      case DioExceptionType.sendTimeout:
        //发送超时处理
        break;
      case DioExceptionType.receiveTimeout:
        //接收超时处理
        break;
      case DioExceptionType.badResponse:
        //服务器响应错误处理处理
        break;
      case DioExceptionType.cancel:
        //请求取消处理
        break;
      case DioExceptionType.connectionError:
        //连接错误处理
        break;
      case DioExceptionType.unknown:
      default:
      //未知错误
    }
    return null;
  }
}

github源码

github.com/mrginpadd/i…

参考文档

这边文章写的非常详细: bbs.huaweicloud.com/blogs/42280… 日志记录 bbs.huaweicloud.com/blogs/44306…

如果有帮到你的话😂顺手给个打赏吧。身体健康,财源滚滚~.

image.png