背景
我们知道dio
是一个非常强大的Dart
Http
请求库,支持非常多的功能,如果我们单单只是在外层包裹一层业务相关的封装,如果业务场景稍微复杂一点,这一层封装就显得臃肿和冗余了,当然我们的网络层本身也是为业务层服务,业务层有了需求,网络层自然得有响应了,只是响应的方式应该遵循低耦合、可用性、可维护性、可扩展性等原则。
设计
我在做iOS原生开发的时候深受Casa大神网络层设计的影响,这里将服务层(service
)和接口层(api
)划分开,这样做的好处有:
- 在面对不同服务时可单独配置相关的
http
强求配置,通常我们的应用在开发的过程中都不会只和一个服务交互,所以在这种场景下是非常有必要的。 - 服务层和接口层职责拆分,让他们做自己该做的事情。
实现
服务层
服务层的职责包括: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
这里为了方便外部设置添加了set
和get
方法:
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
来管理多个Service
,manager
为一个单例,用于注册和管理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
,如有需求自行实现即可:
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) {
/* 请求失败结果处理 */
}
);
}
复制代码