Flutter Dio的分层封装

背景

我们知道dio是一个非常强大的Dart Http请求库,支持非常多的功能,如果我们单单只是在外层包裹一层业务相关的封装,如果业务场景稍微复杂一点,这一层封装就显得臃肿和冗余了,当然我们的网络层本身也是为业务层服务,业务层有了需求,网络层自然得有响应了,只是响应的方式应该遵循低耦合、可用性、可维护性、可扩展性等原则。

设计

我在做iOS原生开发的时候深受Casa大神网络层设计的影响,这里将服务层(service)和接口层(api)划分开,这样做的好处有:

  1. 在面对不同服务时可单独配置相关的http强求配置,通常我们的应用在开发的过程中都不会只和一个服务交互,所以在这种场景下是非常有必要的。
  2. 服务层和接口层职责拆分,让他们做自己该做的事情。

设计.png

实现

服务层

服务层的职责包括:baseUrl的配置、服务公共请求头的配置、服务公共请求参数的配置、请求结果的加工、请求错误的统一处理等,除此之外还有token验证、log输出等。这里只提供一种封装思路具体的实现可以根据业务场景自行实现。 首先我们会创建一个基类,这里命名为Service,首先每一个服务都应该拥有一个Dio实例:

import 'package:dio/dio.dart';

class Service {
  final Dio dio = Dio();
}
复制代码

添加serviceKey

为了更好管理service,这里引入一个service来作为唯一标识。

import 'package:dio/dio.dart';

class Service {
  final Dio dio = Dio();
}
复制代码

添加配置baseUrl

这里为了方便外部设置添加了setget方法:

import 'package:dio/dio.dart';

class Service {
  final Dio dio = Dio();

  late var _baseUrl = "";

  set baseUrl(value) {
    if (_baseUrl == value) {
      return;
    }
    _baseUrl = value;
    dio.options.baseUrl = _baseUrl;
  }

  get baseUrl => _baseUrl;

  String serviceKey() {
    return "";
  }
}
复制代码

添加公共请求头、公共参数

import 'package:dio/dio.dart';

class Service {
  final Dio dio = Dio();

  /*...省略其他设置...*/

  Map<String, dynamic>? serviceHeader() {
    return null;
  }

  Map<String, dynamic>? serviceQuery() {
    return null;
  }

  Map<String, dynamic>? serviceBody() {
    return null;
  }
}
复制代码

添加公共结果处理、错误处理

在请求结果到达后可对Api结果进行统一处理

import 'package:dio/dio.dart';

class Service {
  final Dio dio = Dio();

  /*...省略其他设置...*/

 Map<String, dynamic> responseFactory(Map<String, dynamic> dataMap) {
     /*...可对请求结果进行加工...*/
    return dataMap;
  }
  
  String errorFactory(DioError error) {
    // 请求错误处理
    String errorMessage = error.message;
    /*...具体的错误处理...*/
    return errorMessage;
  }
}
复制代码

Service的管理

这里是引入了一个manager来管理多个Servicemanager为一个单例,用于注册和管理Service,实现也很简单,就使用一个serviceMap来存放Service,在使用服务前调用方法registeredService()注册需要服务即可,如果需要批量注册或者注销等功能自行添加即可。

import 'service.dart';

class ServiceManager {
  static final ServiceManager _instance = ServiceManager._internal();
  factory ServiceManager() {
    return _instance;
  }

  ServiceManager._internal() {
    init();
  }

  Map serviceMap = {};

  void init() {}

  void registeredService(Service service) {
    service.initDio();
    String key = service.serviceKey();
    serviceMap[key] = service;
  }
}

复制代码

Api层

Api层的职责包括设置path、请求参数的组装、请求method配置等功能了。

定义一个自定义的method

enum RequestMethod { get, post, put, delete, patch, copy }
复制代码

serviceKey的配置

设置serviceKey是为了指定该Api属于哪一个服务的:

class BaseApi {
  String serviceKey() {
    return "";
  }
}
复制代码

加上path和method配置

class BaseApi {
  String serviceKey() {
    return "";
  }

  String path() {
    return "";
  }

  RequestMethod method() {
    return RequestMethod.get;
  }
}

复制代码

核心request方法

此处只实现了部分method,如有需求自行实现即可:

扫描二维码关注公众号,回复: 14195775 查看本文章
class BaseApi {

  /*...省略其他部分...*/

  void request(
      {Map<String, dynamic>? query,
      Map<String, dynamic>? body,
      Map<String, dynamic>? header,
      required Function successCallBack,
      required Function errorCallBack}) async {
    //获取到对应的服务
    Service service;
    if (ServiceManager().serviceMap.containsKey(serviceKey())) {
      service = ServiceManager().serviceMap[serviceKey()];
    } else {
      throw Exception('服务尚未注册');
    }
    Dio dio = service.dio;

    Response? response;
    Map<String, dynamic>? queryParams = {};
    var globalQueryParams = service.serviceQuery();
    if (globalQueryParams != null) {
      queryParams.addAll(globalQueryParams);
    }
    if (query != null) {
      queryParams.addAll(query);
    }

    Map<String, dynamic>? headerParams = {};

    var globalHeaderParams = service.serviceHeader();
    if (globalHeaderParams != null) {
      headerParams.addAll(globalHeaderParams);
    }
    if (header != null) {
      headerParams.addAll(header);
    }

    Map<String, dynamic>? bodyParams = {};
    var globalBodyParams = service.serviceBody();
    if (globalBodyParams != null) {
      bodyParams.addAll(globalBodyParams);
    }
    if (body != null) {
      bodyParams.addAll(body);
    }

    String url = path();

    Options options = Options(headers: headerParams);

    try {
      switch (method()) {
        case RequestMethod.get:
          if (queryParams.isNotEmpty) {
            response = await dio.get(url,
                queryParameters: queryParams, options: options);
          } else {
            response = await dio.get(url, options: options);
          }
          break;
        case RequestMethod.post:
          if (body != null && body.isNotEmpty) {
            response = await dio.post(url, data: body, options: options);
          } else {
            response = await dio.post(url, options: options);
          }
          break;
        default:
      }
    } on DioError catch (error) {
      errorCallBack(service.errorFactory(error));
    }
    if (response != null && response.data != null) {
      String dataStr = json.encode(response.data);
      Map<String, dynamic> dataMap = json.decode(dataStr);
      dataMap = service.responseFactory(dataMap);
      successCallBack(dataMap);
    }
  }
}

复制代码

使用

实现服务层

继承与Service完成相应的父类方法重写以及ServiceKey的定义:

const String customServiceKey = "CustomService";

class CustomService extends Service {
  @override
  String serviceKey() {
    return customServiceKey;
  }

  @override
  Map<String, dynamic>? serviceHeader() {
    Map<String, dynamic> header = <String, dynamic>{};
    header["token"] = "...";
    return header;
  }

  //初始化Dio
  @override
  void initDio() {
    dio.options.headers = {
      "Content-Type": 'application/json',
    };
    dio.options.baseUrl = "http://abc/m/1234";
    dio.options.connectTimeout = 15000;
    dio.options.receiveTimeout = 8000;
    dio.options.contentType = "application/json";
    dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
  }
}

复制代码

注册

在网络请求前需要完成服务的注册:

void main() {
  runApp(MyApp());
  //注册服务
  ServiceManager().registeredService(CustomService());
}
复制代码

Api的实现

Api的实现也是继承于BaseApi,然后实现相关的父类方法重写即可:

class CustomApi extends BaseApi {
  @override
  String path() {
    return "/abc/api1";
  }

  @override
  RequestMethod method() {
    return RequestMethod.post;
  }

  @override
  String serviceKey() {
    //返回服务中定义的key
    return customServiceKey;
  }
}
复制代码

Api的调用

对上面的Api发起请求例子:

void requestApi() {
    CustomApi api = CustomApi();
    Map<String, dynamic> body = {"key": value};
    api.request(
        body: body,
        successCallBack: (data) {
            /* 请求结果处理 */
        },
        errorCallBack: (error) {
            /* 请求失败结果处理 */
       }
    );
}
复制代码

demo

猜你喜欢

转载自juejin.im/post/7101238139254997006