Flutter 13 网络层框架架构设计,支持dio等框架。
在移动APP开发过程中,进行数据交互时,大多数情况下必须通过网络请求来实现。客户端与服务端常用的数据交互是通过HTTP请求完成。面对繁琐业务网络层,我们该如何通过网络层架构设计来有效解决这些问题,这便是网络层框架架构设计的初衷。
设计要求:
1. 支持网络库插拔设计,且不干扰业务层
2. 简洁易用,支持配置来进行请求
3. Adapter设计,扩展性强
4. 统一异常和返回处理
解决问题:
切换成本高:网络操作使用的三方库存在不维护切换成本高的风险;
接口管理不便:对于大中型APP接口众多,不方便管理;
重复代码多:APP中进行数据交互的场景很多,网络请求存在大量的重复代码;
扩展性差:网络操作和业务代码耦合严重,不利于扩展;
开发效率低:不同三方库使用方式不统一,步骤繁琐开发效率低;
一、搭建基础的网络请求框架HiNet
1)创建基础请求
创建抽象的基础请求类BaseRequest,并向上层提供获取请求路径,请求方式等抽象方法,及添加参数和请求头等能力。
base_request.dart
enum HttpMethod { GET, POST, DELETE }
/// 基础请求
abstract class BaseRequest {
var pathParams;
var userHttps = true;
/// 域名
String authority() {
return "api.devio.org";
}
HttpMethod httpMethod();
String path();
String url() {
Uri uri;
var pathStr = path();
// 拼接路径参数
if (pathParams != null) {
if (pathStr.endsWith("/")) {
pathStr = "$pathStr$pathParams";
} else {
pathStr = "$pathStr/$pathParams";
}
}
// http和https的切换
if (userHttps) {
uri = Uri.https(authority(), pathStr, params);
} else {
uri = Uri.http(authority(), pathStr, params);
}
print("url:${uri.toString()}");
return uri.toString();
}
bool needLogin();
Map<String, String> params = {};
/// 添加参数
BaseRequest add(String k, Object v) {
params[k] = v.toString();
return this;
}
Map<String, dynamic> header = {};
/// 添加请求头
BaseRequest addHeader(String k, Object v) {
header[k] = v.toString();
return this;
}
}
2)创建测试请求
创建测试请求类TextRequest继承自 基础请求抽象类BaseRequest,实现请求路径和请求方式等。
test_request.dart
import 'package:hi_net/http/request/base_request.dart';
class TestRequest extends BaseRequest{
@override
HttpMethod httpMethod() {
return HttpMethod.GET;
}
@override
bool needLogin() {
return false;
}
@override
String path() {
return "uapi/test/test";
}
}
3)创建核心网络请求类
创建核心网络请求类HiNet,提供发送请求,接收响应,解析返回数据,统一错误处理等功能。当前使用模拟响应请求的方式,下面内容会带着大家一步一步进行完善。
hi_net.dart
import 'package:hi_net/http/request/base_request.dart';
class HiNet {
HiNet._internal();
static HiNet? _instance;
static HiNet get getInstance {
_instance ??= HiNet._internal();
return _instance!;
}
Future fire(BaseRequest request) async {
var respones = await send(request);
var result = respones['data'];
print("result:$result");
return result;
}
Future<dynamic> send<T>(BaseRequest request) {
print("url:${request.url()}");
print("httpMethod:${request.httpMethod()}");
request.addHeader("aaaa", "bbbb");
print("header:${request.header}");
return Future.value({
"statusCode": 200,
"data": {"code": 0, "message": "success"}
});
}
}
4)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行模拟Http请求。
TestRequest testRequest = TestRequest();
testRequest.add("1111", "2222").add("3333", "4444");
HiNet.getInstance.fire(testRequest);
模拟接口请求成功,输出log:
二、增加统一异常和响应数据处理,及Adapter模式设计
1)创建网络异常统一格式类
创建网络异常统一格式类HiNetError,包含code、message和data信息。
创建登录异常NeedLogin 和 授权异常NeedAuth 继承自HiNetError。
hi_net_error.dart
/// 需要登录的异常
class NeedLogin extends HiNetError {
NeedLogin({int code = 401, String message = "请先登录"}) : super(code, message);
}
/// 需要授权的异常
class NeedAuth extends HiNetError {
NeedAuth(String message, {int code = 403, dynamic data})
: super(code, message, data: data);
}
/// 网络异常统一格式类
class HiNetError implements Exception {
final int code;
final String message;
final dynamic data;
HiNetError(this.code, this.message, {this.data});
}
2)创建统一网络层返回格式
创建统一网络层返回格式HiNetResponse,包含request、statusCode、statusMessage和data等信息。
hi_net_adapter.dart
/// 统一网络层返回格式
class HiNetResponse<T> {
HiNetResponse(
{this.data,
this.request,
this.statusCode,
this.statusMessage,
this.extra});
T? data;
BaseRequest? request;
int? statusCode;
String? statusMessage;
dynamic extra;
@override
String toString() {
if (data is Map) {
return json.encode(data);
}
return data.toString();
}
}
3)创建网络请求抽象类
网络请求抽象类HiNetAdapter,提供发送请求能力。
hi_net_adapter.dart
import 'dart:convert';
import 'package:hi_net/http/request/base_request.dart';
/// 网络请求抽象类
abstract class HiNetAdapter {
Future<HiNetResponse<T>> send<T>(BaseRequest request);
}
/// 统一网络层返回格式
class HiNetResponse<T> {
HiNetResponse(
{this.data,
this.request,
this.statusCode,
this.statusMessage,
this.extra});
T? data;
BaseRequest? request;
int? statusCode;
String? statusMessage;
dynamic extra;
@override
String toString() {
if (data is Map) {
return json.encode(data);
}
return data.toString();
}
}
4)创建测试适配器
创建测试适配器MockAdapter ,mock数据。
mock_adapter.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';
/// 测试适配器,mock数据
class MockAdapter extends HiNetAdapter {
@override
Future<HiNetResponse<T>> send<T>(BaseRequest request) {
return Future.delayed(const Duration(milliseconds: 1000), () {
return HiNetResponse(
data: {"code": 0, "message": "success"} as T, statusCode: 200);
});
}
}
5)完善核心网络请求类
完善核心网络请求类HiNet,使用mock适配器MockAdapter发送请求,使用HiNetResponse接收请求响应数据,增加统一错误处理。
hi_net.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/core/mock_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';
class HiNet {
HiNet._internal();
static HiNet? _instance;
static HiNet get getInstance {
_instance ??= HiNet._internal();
return _instance!;
}
Future fire(BaseRequest request) async {
HiNetResponse? response;
var error;
try {
response = await send(request);
} on HiNetError catch (e) {
error = e;
response = e.data;
print("HiNetError:${e.message}");
} catch (e) {
// 其他错误
error = e;
print("OtherError:$e");
}
if (response == null) {
print("error:$error");
}
var result = response?.data;
print("result:$result");
var status = response?.statusCode ?? 0;
switch (status) {
case 200:
return result;
case 401:
throw NeedLogin();
case 403:
throw NeedAuth(result.toString(), data: result);
default:
throw HiNetError(status, result.toString(), data: result);
}
}
Future<dynamic> send<T>(BaseRequest request) {
print("url:${request.url()}");
/// 使用mock发送请求
HiNetAdapter adapter = MockAdapter();
return adapter.send(request);
}
}
6)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行模拟Http请求。
TestRequest testRequest = TestRequest();
testRequest.add("1111", "2222").add("3333", "4444");
try{
var result = await HiNet.getInstance.fire(testRequest);
print(result);
} on NeedAuth catch (e) {
print(e);
} on NeedAuth catch (e) {
print(e);
} on HiNetError catch (e) {
print(e);
}
模拟接口请求成功,输出log:
三、 扩展hi_net添加对dio的支持
1)创建dio适配器
创建dio适配器DioAdapter,重写发送请求的send方法,采用dio框架进行真正的Http网络请求;根据服务器响应数据构建HiNetError 和 HiNetResponse。
添加dio依赖:
pubspec.yaml
dio: ^5.7.0
dio_adapter.dart
import 'package:dio/dio.dart';
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/request/base_request.dart';
/// Dio适配器
class DioAdapter extends HiNetAdapter {
@override
Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
Response? response;
var error, options = Options(headers: request.header);
try {
if (request.httpMethod() == HttpMethod.GET) {
response = await Dio().get(request.url(), options: options);
} else if (request.httpMethod() == HttpMethod.POST) {
response = await Dio()
.post(request.url(), data: request.params, options: options);
} else if (request.httpMethod() == HttpMethod.DELETE) {
response = await Dio()
.delete(request.url(), data: request.params, options: options);
}
} on DioError catch (e) {
error = e;
response = e.response;
}
if (error != null) {
/// 抛出HiNetError异常
throw HiNetError(response?.statusCode ?? -1, error.toString(),
data: buildResponse(response, request));
}
return buildResponse(response, request);
}
/// 构建HiNetResponse
HiNetResponse<T> buildResponse<T>(Response? response, BaseRequest request) {
return HiNetResponse(
data: response?.data as T,
request: request,
statusCode: response?.statusCode,
statusMessage: response?.statusMessage,
extra: response);
}
}
2)使用dio发送请求
修改HiNet 的send方法,使用dio适配器发送请求;Adapter设计,可轻便的更换三方网络请求库,加强了网络请求架构的扩展性。
hi_net.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/core/adapter/mock_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';
import 'adapter/dio_adapter.dart';
///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {
Future<dynamic> send<T>(BaseRequest request) {
print("url:${request.url()}");
/// 使用mock发送请求
/// HiNetAdapter adapter = MockAdapter();
/// 使用dio发送请求
HiNetAdapter adapter = DioAdapter();
return adapter.send(request);
}
}
3)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行Http请求。
注意:该测试接口requestPrams是必传字段,不传接口会返回失败。
TestRequest testRequest = TestRequest();
testRequest.add("1111", "2222").add("3333", "4444").add("requestPrams", "5555");
try{
var result = await HiNet.getInstance.fire(testRequest);
print("testRequest result: $result");
} on NeedAuth catch (e) {
print(e);
} on NeedAuth catch (e) {
print(e);
} on HiNetError catch (e) {
print(e);
}
Http接口请求成功,输出log:
Http接口请求失败,输出log:
四、JSON编码器和解码器
1)使用 json_serializable 框架
使用 json_serializable 框架,对JSON数据进行解析。
添加 json_serializable 依赖:
pubspec.yaml
json_serializable: ^6.8.0
json_annotation: ^4.9.0
build_runner: ^2.1.11
2)创建测试接口返回数据bean
测试接口返回接送数据:
{code: 0, method: GET, requestPrams: 5555}
创建TestModel,编写好属性、构造方法、TestModel.fromJson() 和 toJson();增加注解:@JsonSerializable()
test_model.dart
import 'package:json_annotation/json_annotation.dart';
part 'test_model.g.dart';
/// 测试接口返回数据bean
/// {code: 0, method: GET, requestPrams: 5555}
@JsonSerializable()
class TestModel {
int code;
String method;
String requestPrams;
TestModel(this.code, this.method, this.requestPrams);
//固定格式,不同的类使用不同的mixin即可
factory TestModel.fromJson(Map<String, dynamic> json) => _$TestModelFromJson(json);
//固定格式,
Map<String, dynamic> toJson() => _$TestModelToJson(this);
}
执行 :dart run build_runner build
提示:需要在 TestModel 增加 part 'test_model.g.dart';
加上后再次执行 :dart run build_runner build ,接口自动生成 test_model.g.dart 文件
总结:使用 json_serializable 框架构建JSON解析类时,手动创建的基类Model(TestModel),必须满足以下要求:
1. 在Model类编写 part '同Model类文件名.g.dart'; 如 TestModel类文件名est_model.dart ,则编写 part 'test_model.g.dart';
2.增加注解: @JsonSerializable()
3.编写固定格式的代码,名字换成基类名字,这里是TestModel:
//固定格式,不同的类使用不同的mixin即可
factory TestModel.fromJson(Map<String, dynamic> json) => _$TestModelFromJson(json);
//固定格式,
Map<String, dynamic> toJson() => _$TestModelToJson(this);
3)使用
TestRequest testRequest = TestRequest();
testRequest.add("1111", "2222").add("3333", "4444").add("requestPrams", "5555");
try{
var result = await HiNet.getInstance.fire(testRequest);
var testModel = TestModel.fromJson(result['data']);
print("testRequest requestPrams: ${testModel.requestPrams}");
} on NeedAuth catch (e) {
print(e);
} on NeedAuth catch (e) {
print(e);
} on HiNetError catch (e) {
print(e);
}
JSON解析,输出log:
至此,完成了网络请求框架的基本功能,持续完善。。。