美文网首页Flutterflutter
Flutter的Dio网络请求封装

Flutter的Dio网络请求封装

作者: 漆先生 | 来源:发表于2022-07-15 17:58 被阅读0次

    一、添加依赖

    在yaml文件里边添加如下依赖

    dependencies:
      dio: ^4.0.4
    

    二、添加配置文件

    新建一个network_config.dart文件存放网络配置

    class NetWorkConfig {
      static String baseUrl =
          "https://www.fastmock.site/mock/d8bfa0e1c67bfd329a33ae3294d98c78/test";
      static const connectTimeOut = 10000;
      static const readTimeOut = 10000;
      static const writeTimeOut = 10000;
      static const successCode = 200;
    }
    

    三、请求封装

    ApiResponse是之前定义的公共接口返回实体Flutter的Json数据解析之FlutterJsonBeanFactory插件

    import 'dart:convert';
    
    import 'package:dio/dio.dart';
    import 'package:flutter_core/network/network_config.dart';
    
    import '../models/api_response/api_response_entity.dart';
    import '../models/api_response/raw_data.dart';
    import 'exception.dart';
    
    class RequestClient {
      late Dio _dio;
      static final RequestClient _singletonRequestClient =
          RequestClient._internal();
    
      factory RequestClient() {
        return _singletonRequestClient;
      }
    
      RequestClient._internal() {
        ///初始化 dio 配置
        var options = BaseOptions(
            baseUrl: NetWorkConfig.baseUrl,
            connectTimeout: NetWorkConfig.connectTimeOut,
            receiveTimeout: NetWorkConfig.readTimeOut,
            sendTimeout: NetWorkConfig.writeTimeOut);
        _dio = Dio(options);
      }
    
      /// dio 本身提供了get 、post 、put 、delete 等一系列 http 请求方法,最终都是调用request,直接封装request就行
      Future<T?> request<T>(
        String url, {
        required String method,
        Map<String, dynamic>? queryParameters,
        dynamic data,
        Map<String, dynamic>? headers,
        bool Function(ApiException)? onError, //用于错误信息处理的回调
      }) async {
        try {
          Options options = Options()
            ..method = method
            ..headers = headers;
    
          data = _convertRequestData(data);
    
          Response response = await _dio.request(url,
              queryParameters: queryParameters, data: data, options: options);
    
          return _handleResponse<T>(response);
        } catch (e) {
          ///创建 ApiException ,调用 onError,当 onError 返回为 true 时即错误信息已被调用方处理,则不抛出异常,否则抛出异常。
          var exception = ApiException.from(e);
          if (onError?.call(exception) != true) {
            throw exception;
          }
        }
        return null;
      }
    
      ///将请求 data 数据先使用 jsonEncode 转换为字符串,再使用 jsonDecode 方法将字符串转换为 Map。
      _convertRequestData(data) {
        if (data != null) {
          data = jsonDecode(jsonEncode(data));
        }
        return data;
      }
    
      ///请求响应内容处理
      T? _handleResponse<T>(Response response) {
        if (response.statusCode == 200) {
          //判断泛型是否为 RawData ,是则直接把 response.data 放入 RawData 中返回,
          //即 RawData 的 value 就是接口返回的原始数据。用于第三方平台的接口请求,返回数据接口不支持定义的ApiResponse的情况
          if (T.toString() == (RawData).toString()) {
            RawData raw = RawData();
            raw.value = response.data;
            return raw as T;
          } else {
            ApiResponse<T> apiResponse = ApiResponse<T>.fromJson(response.data);
            return _handleBusinessResponse<T>(apiResponse);
          }
        } else {
          var exception =
              ApiException(response.statusCode, ApiException.unknownException);
          throw exception;
        }
      }
    
      ///业务内容处理
      T? _handleBusinessResponse<T>(ApiResponse<T> response) {
        if (response.code == NetWorkConfig.successCode) {
          return response.data;
        } else {
          var exception = ApiException(response.code, response.message);
          throw exception;
        }
      }
    
      Future<T?> get<T>(
        String url, {
        Map<String, dynamic>? queryParameters,
        Map<String, dynamic>? headers,
        bool showLoading = true,
        bool Function(ApiException)? onError,
      }) {
        return request(url,
            method: "Get",
            queryParameters: queryParameters,
            headers: headers,
            onError: onError);
      }
    
      Future<T?> post<T>(
        String url, {
        Map<String, dynamic>? queryParameters,
        data,
        Map<String, dynamic>? headers,
        bool showLoading = true,
        bool Function(ApiException)? onError,
      }) {
        return request(url,
            method: "POST",
            queryParameters: queryParameters,
            data: data,
            headers: headers,
            onError: onError);
      }
    
      Future<T?> delete<T>(
        String url, {
        Map<String, dynamic>? queryParameters,
        data,
        Map<String, dynamic>? headers,
        bool showLoading = true,
        bool Function(ApiException)? onError,
      }) {
        return request(url,
            method: "DELETE",
            queryParameters: queryParameters,
            data: data,
            headers: headers,
            onError: onError);
      }
    
      Future<T?> put<T>(
        String url, {
        Map<String, dynamic>? queryParameters,
        data,
        Map<String, dynamic>? headers,
        bool showLoading = true,
        bool Function(ApiException)? onError,
      }) {
        return request(url,
            method: "PUT",
            queryParameters: queryParameters,
            data: data,
            headers: headers,
            onError: onError);
      }
    
    }
    
    

    四、异常处理

    主要是对http异常和业务异常进行处理。

    import 'package:dio/dio.dart';
    import 'package:flutter_core/models/api_response/api_response_entity.dart';
    
    class ApiException implements Exception {
      static const unknownException = "未知错误";
      final String? message;
      final int? code;
      String? stackInfo;
    
      ApiException([this.code, this.message]);
    
      factory ApiException.fromDioError(DioError error) {
        switch (error.type) {
          case DioErrorType.cancel:
            return BadRequestException(-1, "请求取消");
          case DioErrorType.connectTimeout:
            return BadRequestException(-1, "连接超时");
          case DioErrorType.sendTimeout:
            return BadRequestException(-1, "请求超时");
          case DioErrorType.receiveTimeout:
            return BadRequestException(-1, "响应超时");
          case DioErrorType.response:
            try {
              /// http错误码带业务错误信息
              ApiResponse apiResponse = ApiResponse.fromJson(error.response?.data);
              if (apiResponse.code != null) {
                return ApiException(apiResponse.code, apiResponse.message);
              }
    
              int? errCode = error.response?.statusCode;
              switch (errCode) {
                case 400:
                  return BadRequestException(errCode, "请求语法错误");
                case 401:
                  return UnauthorisedException(errCode!, "没有权限");
                case 403:
                  return UnauthorisedException(errCode!, "服务器拒绝执行");
                case 404:
                  return UnauthorisedException(errCode!, "无法连接服务器");
                case 405:
                  return UnauthorisedException(errCode!, "请求方法被禁止");
                case 500:
                  return UnauthorisedException(errCode!, "服务器内部错误");
                case 502:
                  return UnauthorisedException(errCode!, "无效的请求");
                case 503:
                  return UnauthorisedException(errCode!, "服务器异常");
                case 505:
                  return UnauthorisedException(errCode!, "不支持HTTP协议请求");
                default:
                  return ApiException(
                      errCode, error.response?.statusMessage ?? '未知错误');
              }
            } on Exception {
              return ApiException(-1, unknownException);
            }
          default:
            return ApiException(-1, error.message);
        }
      }
    
      factory ApiException.from(dynamic exception) {
        if (exception is DioError) {
          return ApiException.fromDioError(exception);
        }
        if (exception is ApiException) {
          return exception;
        } else {
          var apiException = ApiException(-1, unknownException);
          apiException.stackInfo = exception?.toString();
          return apiException;
        }
      }
    }
    
    /// 请求错误
    class BadRequestException extends ApiException {
      BadRequestException([int? code, String? message]) : super(code, message);
    }
    
    /// 未认证异常
    class UnauthorisedException extends ApiException {
      UnauthorisedException([int code = -1, String message = ''])
          : super(code, message);
    }
    
    

    五、嵌套请求

    上述封装后,如果业务存在多个请求依赖调用,就需要统一的处理错误。

    //先定义一个顶级的request
    Future request(Function() block,  {bool showLoading = true, bool Function(ApiException)? onError, }) async{
      try {
        await loading(block, isShowLoading:  showLoading);
      } catch (e) {
        handleException(ApiException.from(e), onError: onError);
      }
      return;
    }
    
    //统一的错误处理
    bool handleException(ApiException exception,
        {bool Function(ApiException)? onError}) {
      if (onError?.call(exception) == true) {
        return true;
      }
    //当外部未处理异常时则在 handleException 中进行统一处理
      //todo 错误码统一处理
      // if(exception.code == 401 ){
      //
      //   return true;
      // }
      EasyLoading.showError(exception.message ?? ApiException.unknownException);
      return false;
    }
    
    //请求的dialog
    Future loading( Function block, {bool isShowLoading = true}) async{
      if (isShowLoading) {
        showLoading();
      }
      try {
        await block();
      } catch (e) {
        rethrow;
      } finally {
        dismissLoading();
      }
      return;
    }
    
    
    void showLoading(){
      EasyLoading.show(status: "加载中...");
    }
    
    void dismissLoading(){
      EasyLoading.dismiss();
    }
    

    六、拦截器

    Dio支持自定义拦截器,继承Interceptor,重写onRequestonResponse方法就行。

    class HeadInterceptor extends Interceptor {
      @override
      void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
        super.onRequest(options, handler);
      }
    
      @override
      void onResponse(dio.Response response, ResponseInterceptorHandler handler) {
        super.onResponse(response, handler);
      }
    }
    

    在初始化dio的地方,把拦截器加入dio对象的拦截器集合dio.interceptors中就行。

    七、日志打印

    可以通过自定义的拦截器实现,也可以引入pretty_dio_logger库。

    dependencies:
      pretty_dio_logger: ^1.1.1
    
    _dio.interceptors.add(PrettyDioLogger(
        requestHeader: true,
        requestBody: true,
        responseHeader: true,
        responseBody: true));
    
    image.png

    八、模拟请求

    fastmock上新建自己的项目,接口配置如下:

    {
      code: ({
        _req,
        Mock
      }) => {
        let body = _req.body;
        return body.username === "qi" && body.password === "123456" ?
          200 :
          1000001;
      },
      message: ({
        _req,
        Mock
      }) => {
        let body = _req.body;
        return body.username === "qi" && body.password === "123456" ?
          "success" :
          "请确认账号密码后再次重试";
      },
      data: function({
        _req,
        Mock
      }) {
        let body = _req.body;
        if (body.username === "qi" && body.password === "123456") {
          return Mock.mock({
            username: "qi",
            userId: "123456",
            email: "@email",
            address: "@address",
            "age|10-30": 18,
          });
        } else {
          return null
        }
      },
    };
    

    发起请求:

      void login() =>
          request(() async {
            LoginParams params = LoginParams();
            params.username = "qi";
            params.password = "123456";
            UserEntity? user =
            await apiService.login(params);
            state.user = user;
            debugPrint("-------------${user?.username ?? "登录失败"}");
            update();
          }, onError: (ApiException e) {
            state.errorMessage = "request error : ${e.message}";
            debugPrint(state.errorMessage);
            update();
            return true;
          });
    
      void loginError() =>
          request(() async {
            LoginParams params = LoginParams();
            params.username = "qi";
            params.password = "123";
            UserEntity? user =
            await apiService.login(params);
            state.user = user;
            debugPrint("-------------${user?.username ?? "登录失败"}");
            update();
          },onError: (ApiException e) {
            state.errorMessage = "request error : ${e.message}";
            debugPrint(state.errorMessage);
            update();
            if (e.code == 1000001) {
              return false;
            } else {
              return true;
            }
          });
    

    效果展示:


    image.png
    image.png
    image.png

    参考文章:
    https://juejin.cn/post/7061806192980410382
    https://juejin.cn/post/7043721908801503269

    相关文章

      网友评论

        本文标题:Flutter的Dio网络请求封装

        本文链接:https://www.haomeiwen.com/subject/jarcirtx.html