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: 

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot2.x教程:(十)从Field injection is not recommended谈谈依赖注入

从Field injection is not recommended谈谈依赖注入 1、问题引入2、依赖注入的三种方式2.1、字段注入&#xff08;Field Injection&#xff09;2.2、构造器注入&#xff08;Constructor Injection&#xff09;2.3、setter注入&#xff08;Setter Injection&#xff09; 3、为什…

Nginx的基础架构解析(下)

1. Nginx模块 1.1 Nginx中的模块化设计 Nginx 的内部结构是由核心部分和一系列的功能模块所组成。这样划分是为了使得每个模块的功能相对简单&#xff0c;便于开发&#xff0c;同时也便于对系统进行功能扩展。Nginx 将各功能模块组织成一条链&#xff0c;当有请求到达的时候&…

【网络】网络层协议IP

目录 IP协议报头 报头分离和向上交付 四位版本 8位服务类型 16位总长度 八位生存时间 16位标识一行 网段划分 DHCP 私有IP范围 公网划分之CIDR 特殊的IP地址 缓解IP地址不够用的方法 NAT技术 路由 IP是用来主机定位和路由选择的&#xff0c;它提供了一种能力&am…

HTML 基础标签——多媒体标签<img>、<object> 与 <embed>

文章目录 1. `<img>` 标签主要属性示例注意事项2. `<object>` 标签概述主要属性示例注意事项3. `<embed>` 标签概述主要属性示例注意事项小结在现代网页设计中,多媒体内容的使用变得越来越重要,因为它能够有效增强用户体验、吸引注意力并传达信息。HTML 提…

【Canal 中间件】Canal 实现 MySQL 增量数据的异步缓存更新

文章目录 一、安装 MySQL1.1 启动 mysql 服务器1.2 开启 Binlog 写入功能1.2.1创建 binlog 配置文件1.2.2 修改配置文件权限1.2.3 挂载配置文件1.2.4 检测 binlog 配置是否成功 1.3 创建账户并授权 二、安装 RocketMQ2.1 创建容器共享网络2.2 启动 NameServer2.3 启动 Broker2.…

深度学习(九):推荐系统的新引擎(9/10)

一、深度学习与推荐系统的融合 深度学习在推荐系统中的融合并非偶然。随着互联网的飞速发展&#xff0c;数据量呈爆炸式增长&#xff0c;传统推荐系统面临着诸多挑战。例如&#xff0c;在处理大规模、高维度的数据时&#xff0c;传统方法往往显得力不从心。而深度学习以其强大的…

masm汇编字符串输出演示

assume cs:code, ds:datadata segmentmassage db zhouzunjie, 0dh, 0ah, $ data endscode segmentstart:mov ax, datamov ds, axmov ah, 09hlea dx, massageint 21hmov ax, 4c00hint 21hcode ends end start 效果演示&#xff1a;

在昇腾Ascend 910B上运行Qwen2.5推理

目前在国产 AI 芯片&#xff0c;例如昇腾 NPU 上运行大模型是一项广泛且迫切的需求&#xff0c;然而当前的生态还远未成熟。从底层芯片的算力性能、计算架构的算子优化&#xff0c;到上层推理框架对各种模型的支持及推理加速&#xff0c;仍有很多需要完善的地方。 今天带来一篇…

HarmonyOS一次开发多端部署三巨头之界面级一多开发

界面级一多开发 引言1. 布局能力1.1 自适应布局1.1.1 拉伸能力1.1.2 均分能力1.1.3 占比能力1.1.4 缩放能力1.1.5延伸能力1.1.6 隐藏能力1.1.7 折行能力 1.2 响应式布局1.2.1 断点和媒体查询1.2.2 栅格布局 2. 视觉风格2.1 分层参数2.2 自定义资源 3. 交互归一4. IDE多设备预览…

(58)LMS自适应滤波算法与系统辨识的MATLAB仿真

文章目录 前言一、LMS算法的基本步骤二、LMS算法的一些主要应用1. 通信系统2. 信号分离与增强3. 控制系统4. 生物医学信号处理5. 机器学习与模式识别6. 其他应用 三、LMS算法用于系统辨识的MATLAB仿真四、仿真结果 前言 LMS&#xff08;Least Mean Squares&#xff0c;最小均方…

bootstrap应用1——计算n从1-100000的每个整数,第j个观测在自助法样本里的概率。

计算n从1-100000的每个整数&#xff0c;第j个观测在自助法样本里的概率。 pr function(n) return(1 - (1 - 1/n)^n) x 1:10000 plot(x, pr(x))

AI-基本概念-向量、矩阵、张量

1 需求 需求&#xff1a;Tensor、NumPy 区别 需求&#xff1a;向量、矩阵、张量 区别 2 接口 3 示例 4 参考资料 【PyTorch】PyTorch基础知识——张量_pytorch张量-CSDN博客

【设计模式】策略模式定义及其实现代码示例

文章目录 一、策略模式1.1 策略模式的定义1.2 策略模式的参与者1.3 策略模式的优点1.4 策略模式的缺点1.5 策略模式的使用场景 二、策略模式简单实现2.1 案例描述2.2 实现代码 三、策略模式的代码优化3.1 优化思路3.2 抽象策略接口3.3 上下文3.4 具体策略实现类3.5 测试 参考资…

2025年PMP考试的3A好考吗?

确实&#xff0c;PMP正式抛弃第六版用第七版教材了&#xff0c;但是考纲还是跟24年一样的&#xff0c;情景题多&#xff0c;考的比之前灵活&#xff0c;但是 3A 的人也不少&#xff0c;按照机构的计划来学习并没有很难&#xff0c;给大家说说我的备考经历吧&#xff0c;希望对你…

VScode + PlatformIO 了解

​Visual Studio Code Visual Studio Code&#xff08;简称 VS Code&#xff09;是一款由微软开发且跨平台的免费源代码编辑器。该软件以扩展的方式支持语法高亮、代码自动补全&#xff08;又称 IntelliSense&#xff09;、代码重构功能&#xff0c;并且内置了工具和 Git 版本…

完美日记营销模式对开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序的启示

摘要&#xff1a;本文通过分析完美日记在营销中利用社会基础设施升级红利、网红与新流量平台、KOL 和私域流量等策略取得成功的案例&#xff0c;探讨其对开源 AI 智能名片 2 1 链动模式 S2B2C 商城小程序在营销推广、用户获取与留存、提升复购率等方面的启示&#xff0c;为商城…

Failed to install Visual Studio Code update

当关闭vsCode的时候&#xff0c;出现了下面的报错&#xff1a; 可能是之前将vscode文件换了位置导致的&#xff0c;并且vscode在桌面的图标也变成了下面这个&#xff1a; 解决方法&#xff1a; 找到上图路径的log文件并打开&#xff1a; 搜索电脑中的Code.exe文件 并粘贴到上…

python在word的页脚插入页码

1、插入简易页码 import win32com.client as win32 from win32com.client import constants import osdoc_app win32.gencache.EnsureDispatch(Word.Application)#打开word应用程序 doc_app.Visible Truedoc doc_app.Documents.Add() footer doc.Sections(1).Footers(cons…

Rust 力扣 - 73. 矩阵置零

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们使用两个变量记录矩阵初始状态的第一行与第一列是否存在0 然后我们遍历矩阵&#xff08;跳过第一行与第一列&#xff09;&#xff0c;如果矩阵中元素为0则将该元素映射到矩阵第一行与矩阵第一列的位置置为0…

Python | Leetcode Python题解之第537题复数乘法

题目&#xff1a; 题解&#xff1a; class Solution:def complexNumberMultiply(self, num1: str, num2: str) -> str:real1, imag1 map(int, num1[:-1].split())real2, imag2 map(int, num2[:-1].split())return f{real1 * real2 - imag1 * imag2}{real1 * imag2 imag1…