Flutterflutter

Flutter的Dio网络请求封装

2022-07-15  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读