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{@overrideHttpMethod httpMethod() {return HttpMethod.GET;}@overridebool needLogin() {return false;}@overrideString 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;@overrideString 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;@overrideString 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 {@overrideFuture<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 {@overrideFuture<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);}/// 构建HiNetResponseHiNetResponse<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.0json_annotation: ^4.9.0build_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: 

至此,完成了网络请求框架的基本功能,持续完善。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/58674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt限制QGraphicsScene QGraphicsItem内部的移动范围

用过QGraphicsView的都知道&#xff0c;原点一般设定在view和item的中心&#xff0c;所以帮助文档和这个网友说的不一定跟我们对的上&#xff1a; 关于Qt限制QGraphicsScene内部Item的移动范围_qgraphicsitem限制移动范围-CSDN博客 首先&#xff0c;设定view的scenerect&…

【数据结构-邻项消除】力扣2211. 统计道路上的碰撞次数

在一条无限长的公路上有 n 辆汽车正在行驶。汽车按从左到右的顺序按从 0 到 n - 1 编号&#xff0c;每辆车都在一个 独特的 位置。 给你一个下标从 0 开始的字符串 directions &#xff0c;长度为 n 。directions[i] 可以是 ‘L’、‘R’ 或 ‘S’ 分别表示第 i 辆车是向 左 、…

强网拟态的复现

web ez_picker 这题的考点 capoo Misc ezflag 这题涉及到kali的工具使用&#xff0c;binwalk或者formost 1.将压缩包拖进010&#xff0c;发现包含flag.zip文件&#xff0c;想到使用工具进行分离 命令&#xff1a;binwalk -e .... --run-asroot 分离流量包 得到一个文件&…

【Effective C++】阅读笔记2

1. 复制对象时要保证复制内容完整性 错误场景复现&#xff08;没有复制基类部分&#xff09; 如果一个类中包含多个成员变量或者继承了基类&#xff0c;那么在拷贝构造函数或者赋值运算符中&#xff0c;必须保证所有成员的基类部分被复制。 基类没有被复制&#xff0c;这样就都…

数组移除元素

目录 题目方法一&#xff1a;直接(暴力)求解法思路分析代码如下 方法二&#xff1a;双指针&#xff08;快慢指针&#xff09;思路分析代码如下 题目 这道题是来自于leetcode的一道算法题&#xff1a; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于…

【Flask】二、Flask 路由机制

目录 什么是路由&#xff1f; Flask中的路由 基本路由 动态路由 路由中的HTTP方法 路由函数返回 在Web开发中&#xff0c;路由是将URL映射到相应的处理函数的过程。Flask是一个轻量级的Web应用框架&#xff0c;提供了简单而强大的路由机制&#xff0c;使得开发者能够轻松…

【IC验证_systemverilog】信号类型

IC验证_systemverilog 1.信号声明2.变量类型3.数据类型4.符号 1.信号声明 语法&#xff1a; 变量类型 信号类型 符号转换 位宽 信号名 深度&#xff1b;2.变量类型 &#xff08;1&#xff09;说明&#xff1a; systemverilog中的信号类型主要分为线网类型&#xff08;wire&a…

《模拟电子技术基础》第六版PDF课后题答案详解

《模拟电子技术基础》第六版是在获首届全国优秀教材建设奖一等奖的第五版的基础上&#xff0c;总结6年来的教学实践经验修订而成的新形态教材。为满足国家人才培养的需求&#xff0c;适应新型教学模式&#xff0c;并考虑到大多数院校逐渐减少课程学时的现状&#xff0c;在不降低…

DOM---鼠标事件类型(移入移出)

移入 移出mouseenter mouseleave 在父盒子上触发&#xff0c;在孩子盒子上不触发 移入 移出mouseover mouseout全触发 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Comp…

网络编程 UDP编程 Linux环境 C语言实现

UDP编程 1. 一般UDP编程 UDP传输特点&#xff1a;非面向连接、不可靠的、无序的 报式传输 支持组播和广播 UDP应用数据最大长度建议&#xff1a;MTU(以太网分组数据的最大长度)1500 - 20(IP头) - 8(UDP头) 1472Bytes 客户端&#xff1a;支持两种形式的代码编写: 1. 不定向…

在manjaro 2024里使用yay命令安装ROS2

不建议这么安装&#xff0c;研究了两天以失败告终。要不就手动编译吧。。。&#xff08;在系统环境良好的情况下&#xff0c;最好是刚装完系统就装ROS&#xff09;真的太多不适配了&#xff0c;旧有的很多yay包都会遇到一些奇怪的问题&#xff1a; 0.一开始就会遇到网络卡住的…

人工智能原理实验一:知识的表示与推理实验

一、实验目的 本实验课程是计算机、智能、物联网等专业学生的一门专业课程&#xff0c;通过实验&#xff0c;帮助学生更好地掌握人工智能相关概念、技术、原理、应用等&#xff1b;通过实验提高学生编写实验报告、总结实验结果的能力&#xff1b;使学生对智能程序、智能算法等有…

自研小程序-心情追忆

在近期从繁忙的工作中暂时抽身之后&#xff0c;我决定利用这段宝贵的时间来保持我的Java技能不致生疏&#xff0c;并通过一个个人项目来探索人工智能的魅力。 我在Hugging Face&#xff08;国内镜像站点&#xff1a;HF-Mirror&#xff09;上发现了一个关于情感分析的练习项目&…

贪心算法入门(一)

1.什么是贪心算法&#xff1f; 贪心算法是一种解决问题的策略&#xff0c;它将复杂的问题分解为若干个步骤&#xff0c;并在每一步都选择当前最优的解决方案&#xff0c;最终希望能得到全局最优解。这种策略的核心在于“最优”二字&#xff0c;意味着我们追求的是以最少的时间和…

FreeSWITCH 简单图形化界面30 - 使用MYODBC时可能遇到的错误

FreeSWITCH 简单图形化界面30 - 使用MYODBC时可能遇到的错误 测试环境1、 MYODBC 3.51.18 or higher2、分析和解决2.1 解决1&#xff0c;降级MySQL ODBC2.2 解决2&#xff0c;修改FreeSWITCH代码 测试环境 http://myfs.f3322.net:8020/ 用户名&#xff1a;admin&#xff0c;密…

四、k8s快速入门之Kubernetes资源清单

kubernetes中的资源 ⭐️ k8s中所有的内容都抽象为资源&#xff0c;资源实列化之后&#xff0c;叫做对象 1️⃣名称空间级别 ⭐️ kubeadm在执行k8s的pod的时候会在kube-system这个名称空间下执行&#xff0c;所以说当你kubectl get pod 的时候是查看不到的查看的是默认的po…

Java面试经典 150 题.P88. 合并两个有序数组(001)

本题来自&#xff1a;力扣-面试经典 150 题 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台https://leetcode.cn/studyplan/top-interview-150/ 题解 class Solution {public void merge(int[] nums1, int m, int[] nums2, …

[LeetCode-55]基于贪心算法的跳跃游戏的求解(C语言版)

/* 题目出处&#xff1a;LeetCode 题目序号&#xff1a;55. 跳跃游戏 题目叙述&#xff1a;给你一个非负整数数组 nums &#xff0c;你最初位于数组的第一个下标位置 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标&#xff0c;如果可…

FPGA时序分析和约束学习笔记(3、Timequest时序路径详解和优化)

FPGA时序分析和约束学习笔记&#xff08;3、Timequest时序路径详解和优化&#xff09; Timequest中Data Path分析 Data Arrival Path clock path&#xff1a;时钟信号到达源寄存器时钟端口的时间 data path&#xff1a;数据从源寄存器Q端口出发到达目标寄存器D端口的时间 D…

四、鸿蒙开发-常用布局(线性布局、层叠布局、弹性布局、网格布局、列表布局)

提示&#xff1a;本文根据b站尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程课整理 链接指引 > 尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程 文章目录 一、布局基础1.1 盒子模型 二、线性布局&#xff08;Column/Row&#xff09;2.1 概述2.2…