Flutter网络请求进阶:请求加密、并发控制与异常监控实战

在Flutter应用的网络交互场景中,除了离线缓存与弱网适配,“数据安全”“请求有序性”“问题可追溯性”同样是保障应用稳定性与用户信任的核心需求。未加密的请求易导致数据泄露,无控制的并发请求可能引发接口雪崩,缺乏监控的异常会让问题排查陷入困境。本文将聚焦这三大核心痛点,展开三大实战内容:请求全链路加密(请求头+请求体+响应体)、基于令牌桶的并发控制、异常分级监控与日志上报,帮助开发者构建“安全、可控、可追溯”的高级网络交互体系。

一、核心认知:进阶网络优化的设计原则

在动手实现前,需明确三大核心设计原则,避免陷入“加密冗余”“控制僵化”“监控泛滥”等坑:

  • 安全分级原则:根据数据敏感度分级加密。核心数据(如用户密码、支付信息)采用非对称加密+对称加密组合方案;一般数据(如列表查询)采用对称加密即可,平衡安全性与性能。

  • 并发适配原则:并发控制需适配业务场景。高频接口(如商品列表下拉刷新)采用宽松的并发限制,核心接口(如订单提交)采用严格的串行执行,避免一刀切的控制策略。

  • 监控精准原则:异常监控需分级分类。区分“致命异常”(如网络中断、服务器宕机)、“警告异常”(如参数错误、超时)、“轻微异常”(如数据格式不规范),针对性上报与处理,避免日志冗余。

二、请求全链路加密:保障数据传输安全

本节将实现一套可扩展的请求加密方案,覆盖“请求头签名、请求体加密、响应体解密”全链路,基于AES对称加密(高效处理大量数据)与RSA非对称加密(安全传输对称密钥)的组合模式,兼顾安全性与性能。

1. 基础准备:加密工具类实现

封装AES与RSA加密工具,提供统一的加密、解密、签名、验签方法,屏蔽底层加密细节。


import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';
import 'package:pointycastle/asymmetric/rsa.dart';
import 'package:pointycastle/pointycastle.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/signers/rsa_signer.dart';

// 加密工具类(单例)
class EncryptUtil {
  static final EncryptUtil _instance = EncryptUtil._internal();
  factory EncryptUtil() => _instance;
  EncryptUtil._internal();

  // AES相关配置(密钥长度128/256位,IV长度16位)
  static const int _aesKeyLength = 32; // 256位密钥
  static const int _aesIVLength = 16;

  // RSA相关配置(公钥用于加密AES密钥,私钥用于解密AES密钥)
  late RSAAsymmetricKey _rsaPublicKey;
  late RSAAsymmetricKey _rsaPrivateKey;

  // 初始化RSA密钥对(实际项目中,公钥由服务端提供,私钥本地安全存储)
  void initRSAKeyPair({required String publicKeyStr, required String privateKeyStr}) {
    _rsaPublicKey = _parseRsaPublicKey(publicKeyStr);
    _rsaPrivateKey = _parseRsaPrivateKey(privateKeyStr);
  }

  // 解析RSA公钥(PEM格式)
  RSAAsymmetricKey _parseRsaPublicKey(String publicKeyStr) {
    final publicKeyPem = publicKeyStr
        .replaceAll('-----BEGIN PUBLIC KEY-----', '')
        .replaceAll('-----END PUBLIC KEY-----', '')
        .replaceAll('\n', '');
    final publicKeyBytes = base64.decode(publicKeyPem);
    final keyParams = RSAKeyParameters.fromPublicKeyBytes(publicKeyBytes, BigInt.from(65537));
    return RSAPublicKey(keyParams.modulus!, keyParams.exponent!);
  }

  // 解析RSA私钥(PEM格式)
  RSAAsymmetricKey _parseRsaPrivateKey(String privateKeyStr) {
    final privateKeyPem = privateKeyStr
        .replaceAll('-----BEGIN PRIVATE KEY-----', '')
        .replaceAll('-----END PRIVATE KEY-----', '')
        .replaceAll('\n', '');
    final privateKeyBytes = base64.decode(privateKeyPem);
    final keyParams = RSAKeyParameters.fromPrivateKeyBytes(privateKeyBytes);
    return RSAPrivateKey(keyParams.modulus!, keyParams.exponent!, keyParams.p!, keyParams.q!);
  }

  // 生成随机AES密钥和IV
  Map<String, String> generateAesKeyAndIV() {
    final key = Uint8List(_aesKeyLength);
    final iv = Uint8List(_aesIVLength);
    // 生成随机字节(实际项目中需使用安全的随机数生成器)
    final random = SecureRandom('AES/CTR/AUTO-SEED-PRNG');
    random.seed(KeyParameter.fromSecureRandom(16));
    random.nextBytes(key);
    random.nextBytes(iv);
    return {
      'key': base64.encode(key),
      'iv': base64.encode(iv),
    };
  }

  // AES加密(CBC模式,PKCS7填充)
  String aesEncrypt(String plainText, {required String keyStr, required String ivStr}) {
    final key = Key.fromBase64(keyStr);
    final iv = IV.fromBase64(ivStr);
    final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: 'PKCS7'));
    final encrypted = encrypter.encrypt(plainText, iv: iv);
    return encrypted.base64;
  }

  // AES解密(CBC模式,PKCS7填充)
  String aesDecrypt(String cipherText, {required String keyStr, required String ivStr}) {
    final key = Key.fromBase64(keyStr);
    final iv = IV.fromBase64(ivStr);
    final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: 'PKCS7'));
    final encrypted = Encrypted.fromBase64(cipherText);
    return encrypter.decrypt(encrypted, iv: iv);
  }

  // RSA加密(用于加密AES密钥)
  String rsaEncrypt(String plainText) {
    final inputBytes = utf8.encode(plainText);
    final engine = RSAEngine()
      ..init(true, PublicKeyParameter<RSAAsymmetricKey>(_rsaPublicKey));
    final outputBytes = engine.process(inputBytes);
    return base64.encode(outputBytes);
  }

  // RSA解密(用于解密AES密钥)
  String rsaDecrypt(String cipherText) {
    final inputBytes = base64.decode(cipherText);
    final engine = RSAEngine()
      ..init(false, PrivateKeyParameter<RSAAsymmetricKey>(_rsaPrivateKey));
    final outputBytes = engine.process(inputBytes);
    return utf8.decode(outputBytes);
  }

  // 生成请求签名(MD5:timestamp+nonce+body+secret)
  String generateSign({
    required String timestamp,
    required String nonce,
    required String body,
    required String secret,
  }) {
    final signStr = '$timestamp$nonce$body$secret';
    final md5Bytes = md5.convert(utf8.encode(signStr)).bytes;
    return base64.encode(md5Bytes);
  }

  // 验证响应签名
  bool verifySign({
    required String timestamp,
    required String nonce,
    required String body,
    required String secret,
    required String sign,
  }) {
    final generatedSign = generateSign(
      timestamp: timestamp,
      nonce: nonce,
      body: body,
      secret: secret,
    );
    return generatedSign == sign;
  }
}

2. 实现加密拦截器:全链路自动加密解密

通过Dio拦截器实现请求的自动加密(请求头签名、请求体加密)与响应的自动解密(响应体解密、签名验证),无需在每个请求中手动处理加密逻辑。


import 'dart:math';
import 'package:dio/dio.dart';
import 'encrypt_util.dart';

// 加密拦截器配置
class EncryptInterceptorConfig {
  // 是否开启加密(可动态开关,如调试环境关闭)
  final bool enableEncrypt;
  // 签名密钥(与服务端约定)
  final String signSecret;
  // 需要跳过加密的接口路径(如登录接口可能需要特殊处理)
  final List<String> skipEncryptPaths;

  EncryptInterceptorConfig({
    this.enableEncrypt = true,
    required this.signSecret,
    this.skipEncryptPaths = const [],
  });
}

// 加密拦截器(请求加密+响应解密)
class EncryptInterceptor extends Interceptor {
  final EncryptInterceptorConfig config;
  final EncryptUtil _encryptUtil = EncryptUtil();

  EncryptInterceptor({required this.config});

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    // 1. 检查是否开启加密,或是否跳过当前接口
    if (!config.enableEncrypt || config.skipEncryptPaths.contains(options.path)) {
      handler.next(options);
      return;
    }

    // 2. 生成请求公共参数(timestamp+nonce)
    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    final nonce = _generateNonce(); // 随机字符串,防止重放攻击

    // 3. 处理请求体:加密
    final plainBody = options.data != null ? json.encode(options.data) : '';
    final aesInfo = _encryptUtil.generateAesKeyAndIV();
    final encryptedBody = _encryptUtil.aesEncrypt(
      plainBody,
      keyStr: aesInfo['key']!,
      ivStr: aesInfo['iv']!,
    );

    // 4. 加密AES密钥(使用RSA公钥)
    final encryptedAesKey = _encryptUtil.rsaEncrypt('${aesInfo['key']}:${aesInfo['iv']}');

    // 5. 生成请求签名
    final sign = _encryptUtil.generateSign(
      timestamp: timestamp,
      nonce: nonce,
      body: plainBody,
      secret: config.signSecret,
    );

    // 6. 构建加密后的请求参数
    options.data = {
      'encryptedBody': encryptedBody,
      'encryptedAesKey': encryptedAesKey,
    };

    // 7. 设置请求头(签名+公共参数)
    options.headers.addAll({
      'timestamp': timestamp,
      'nonce': nonce,
      'sign': sign,
      'encryptType': 'AES+RSA', // 告知服务端加密方式
    });

    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    // 1. 检查是否开启加密,或是否跳过当前接口
    if (!config.enableEncrypt || config.skipEncryptPaths.contains(response.requestOptions.path)) {
      handler.next(response);
      return;
    }

    // 2. 验证响应签名
    final timestamp = response.headers.value('timestamp') ?? '';
    final nonce = response.headers.value('nonce') ?? '';
    final sign = response.headers.value('sign') ?? '';
    final responseBody = json.encode(response.data);
    final verifyResult = _encryptUtil.verifySign(
      timestamp: timestamp,
      nonce: nonce,
      body: responseBody,
      secret: config.signSecret,
      sign: sign,
    );
    if (!verifyResult) {
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          type: DioExceptionType.unknown,
          error: '响应签名验证失败,可能存在数据篡改',
        ),
      );
      return;
    }

    // 3. 解密响应体
    try {
      // 从响应中获取加密的AES密钥和加密的响应体
      final encryptedAesKey = response.data['encryptedAesKey'] as String;
      final encryptedBody = response.data['encryptedBody'] as String;

      // 解密AES密钥(使用RSA私钥)
      final aesKeyAndIV = _encryptUtil.rsaDecrypt(encryptedAesKey);
      final aesKey = aesKeyAndIV.split(':')[0];
      final aesIV = aesKeyAndIV.split(':')[1];

      // 解密响应体(AES)
      final decryptedBody = _encryptUtil.aesDecrypt(
        encryptedBody,
        keyStr: aesKey,
        ivStr: aesIV,
      );

      // 替换响应数据为解密后的数据
      response.data = json.decode(decryptedBody);
      handler.next(response);
    } catch (e) {
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          type: DioExceptionType.unknown,
          error: '响应体解密失败:$e',
        ),
      );
    }
  }

  // 生成随机字符串(16位,用于nonce)
  String _generateNonce() {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    final random = Random.secure();
    return List.generate(16, (index) => chars[random.nextInt(chars.length)]).join();
  }
}

三、并发控制:基于令牌桶算法的请求限流

高频次的并发请求(如下拉刷新+上拉加载同时触发、列表项批量请求)可能导致服务端压力过大、客户端资源占用过高,甚至引发接口雪崩。本节基于令牌桶算法实现请求限流,支持全局并发控制与接口级别的个性化配置。

1. 基础准备:令牌桶算法实现

令牌桶算法核心逻辑:系统以固定速率向桶中添加令牌,请求发起时需从桶中获取令牌,获取到令牌则继续执行,未获取到则等待或拒绝,实现平滑的流量控制。


import 'dart:async';

// 令牌桶(单例,支持动态调整速率和容量)
class TokenBucket {
  static final TokenBucket _instance = TokenBucket._internal();
  factory TokenBucket() => _instance;
  TokenBucket._internal();

  // 桶容量(最大令牌数)
  int _capacity = 10;
  // 令牌生成速率(令牌/秒)
  double _rate = 5;
  // 当前桶中令牌数
  double _currentTokens = 0;
  // 上次令牌生成时间(毫秒级时间戳)
  int _lastRefillTime = DateTime.now().millisecondsSinceEpoch;
  // 锁对象,保证并发安全
  final Object _lock = Object();

  // 初始化令牌桶
  void init({required int capacity, required double rate}) {
    _capacity = capacity;
    _rate = rate;
    _currentTokens = capacity.toDouble(); // 初始装满令牌
    _lastRefillTime = DateTime.now().millisecondsSinceEpoch;
  }

  // 动态调整令牌生成速率
  void adjustRate(double newRate) {
    if (newRate > 0) {
      _rate = newRate;
    }
  }

  // 尝试获取令牌(返回是否获取成功)
  bool tryAcquire({int tokens = 1}) {
    if (tokens <= 0) return false;
    synchronized(_lock, () {
      // 1. 补充令牌(根据上次补充时间到现在的时长)
      _refillTokens();
      // 2. 检查是否有足够的令牌
      if (_currentTokens >= tokens) {
        _currentTokens -= tokens;
        return true;
      }
      return false;
    });
    return false;
  }

  // 阻塞获取令牌(直到获取到为止)
  Future<void> acquire({int tokens = 1}) async {
    if (tokens <= 0) return;
    while (true) {
      if (tryAcquire(tokens: tokens)) {
        return;
      }
      // 未获取到令牌,短暂等待后重试
      await Future.delayed(const Duration(milliseconds: 50));
    }
  }

  // 补充令牌
  void _refillTokens() {
    final now = DateTime.now().millisecondsSinceEpoch;
    final elapsedTime = (now - _lastRefillTime) / 1000; // 秒数
    if (elapsedTime <= 0) return;

    // 生成新令牌数(速率 * 时间)
    final newTokens = _rate * elapsedTime;
    // 补充令牌,不超过桶容量
    _currentTokens = min(_currentTokens + newTokens, _capacity.toDouble());
    // 更新上次补充时间
    _lastRefillTime = now;
  }

  // 清空令牌桶
  void clear() {
    synchronized(_lock, () {
      _currentTokens = 0;
    });
  }
}

2. 实现并发控制拦截器:请求限流

通过Dio拦截器整合令牌桶算法,实现请求的自动限流。支持全局默认限流策略,也可在单个请求中配置个性化的限流参数(如是否跳过限流、所需令牌数)。


import 'package:dio/dio.dart';
import 'token_bucket.dart';

// 并发控制配置(单个请求的个性化配置)
class ConcurrentControlConfig {
  // 是否跳过限流
  final bool skipLimit;
  // 获取令牌数(默认1个)
  final int tokens;

  ConcurrentControlConfig({
    this.skipLimit = false,
    this.tokens = 1,
  });
}

// 并发控制拦截器(基于令牌桶的请求限流)
class ConcurrentControlInterceptor extends Interceptor {
  final TokenBucket _tokenBucket = TokenBucket();
  // 全局默认是否开启限流
  final bool defaultEnableLimit;

  ConcurrentControlInterceptor({
    this.defaultEnableLimit = true,
  }) {
    // 初始化令牌桶(默认容量10,速率5令牌/秒,可根据业务调整)
    _tokenBucket.init(capacity: 10, rate: 5);
  }

  @override
  Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    // 1. 获取当前请求的并发控制配置
    final requestConfig = options.extra['concurrentControl'] as ConcurrentControlConfig? ??
        ConcurrentControlConfig(skipLimit: !defaultEnableLimit);

    // 2. 检查是否跳过限流
    if (requestConfig.skipLimit) {
      handler.next(options);
      return;
    }

    // 3. 阻塞获取令牌(直到获取到为止)
    await _tokenBucket.acquire(tokens: requestConfig.tokens);

    handler.next(options);
  }

  // 动态调整令牌生成速率(如根据网络状态调整:弱网时降低速率)
  void adjustTokenRate(double newRate) {
    _tokenBucket.adjustRate(newRate);
  }
}

四、异常监控:分级上报与日志记录

完善的异常监控体系能帮助开发者快速定位问题。本节实现“异常分级、日志记录、远程上报”三大功能,支持捕获网络请求全链路的异常(请求加密失败、请求超时、响应解密失败、服务端错误等),并按严重程度进行处理。

1. 基础准备:异常模型与分级枚举

定义网络异常模型,区分异常类型与严重级别,为后续的日志记录与上报提供基础。


import 'package:dio/dio.dart';

// 异常严重级别
enum ExceptionLevel {
  fatal, // 致命异常(如网络中断、服务器宕机,影响核心功能)
  warning, // 警告异常(如参数错误、超时,可恢复)
  info, // 信息异常(如数据格式不规范,不影响功能)
}

// 网络异常模型
class NetworkException {
  // 异常ID(唯一标识)
  final String exceptionId;
  // 异常级别
  final ExceptionLevel level;
  // 异常类型
  final String type;
  // 异常消息
  final String message;
  // 请求信息
  final RequestInfo requestInfo;
  // 异常发生时间(时间戳,毫秒级)
  final int timestamp;
  // 额外信息(如堆栈跟踪)
  final Map<String, dynamic>? extra;

  NetworkException({
    required this.exceptionId,
    required this.level,
    required this.type,
    required this.message,
    required this.requestInfo,
    required this.timestamp,
    this.extra,
  });

  Map<String, dynamic> toJson() {
    return {
      'exceptionId': exceptionId,
      'level': level.name,
      'type': type,
      'message': message,
      'requestInfo': requestInfo.toJson(),
      'timestamp': timestamp,
      'extra': extra,
    };
  }
}

// 请求信息模型
class RequestInfo {
  final String method;
  final String path;
  final String url;
  final Map<String, dynamic>? queryParams;
  final dynamic requestData;
  final Map<String, dynamic>? headers;
  final int? timeout;

  RequestInfo({
    required this.method,
    required this.path,
    required this.url,
    this.queryParams,
    this.requestData,
    this.headers,
    this.timeout,
  });

  Map<String, dynamic> toJson() {
    return {
      'method': method,
      'path': path,
      'url': url,
      'queryParams': queryParams,
      'requestData': requestData,
      'headers': headers,
      'timeout': timeout,
    };
  }
}

// 异常ID生成工具
String generateExceptionId() {
  final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
  final random = DateTime.now().microsecond.toString().padLeft(6, '0');
  return '$timestamp$random';
}

2. 实现异常监控拦截器:捕获与上报

通过Dio拦截器捕获请求全链路的异常,结合异常分级模型进行日志记录与远程上报。支持配置是否开启本地日志、是否开启远程上报,以及上报的异常级别阈值。


import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'network_exception.dart';

// 异常监控配置
class ExceptionMonitorConfig {
  // 是否开启本地日志记录
  final bool enableLocalLog;
  // 是否开启远程上报
  final bool enableRemoteReport;
  // 远程上报的级别阈值(只上报大于等于此级别的异常)
  final ExceptionLevel reportLevelThreshold;
  // 远程上报回调(由业务层实现具体的上报逻辑,如上传到服务器)
  final Future<void> Function(NetworkException exception)? onReport;

  ExceptionMonitorConfig({
    this.enableLocalLog = true,
    this.enableRemoteReport = true,
    this.reportLevelThreshold = ExceptionLevel.warning,
    this.onReport,
  });
}

// 异常监控拦截器
class ExceptionMonitorInterceptor extends Interceptor {
  final ExceptionMonitorConfig config;

  ExceptionMonitorInterceptor({required this.config});

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    try {
      handler.next(options);
    } catch (e) {
      // 捕获请求拦截器中的异常(如加密失败)
      _handleException(
        exception: e,
        requestOptions: options,
        level: ExceptionLevel.warning,
        type: 'RequestInterceptorException',
      );
      handler.reject(DioException(requestOptions: options, error: e));
    }
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    try {
      // 检查响应数据是否规范(示例:要求响应必须是JSON格式)
      if (response.data == null || !(response.data is Map) && !(response.data is List)) {
        throw Exception('响应数据格式不规范,必须是JSON对象或数组');
      }
      handler.next(response);
    } catch (e) {
      // 捕获响应拦截器中的异常(如数据格式校验失败)
      _handleException(
        exception: e,
        requestOptions: response.requestOptions,
        level: ExceptionLevel.info,
        type: 'ResponseDataException',
      );
      handler.next(response); // 数据格式不规范不中断流程,仅记录异常
    }
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // 分类处理Dio异常
    ExceptionLevel level;
    String type;
    String message;

    switch (err.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        level = ExceptionLevel.warning;
        type = 'TimeoutException';
        message = '请求超时';
        break;
      case DioExceptionType.connectionError:
        level = ExceptionLevel.fatal;
        type = 'ConnectionException';
        message = '网络连接失败';
        break;
      case DioExceptionType.response:
        level = ExceptionLevel.warning;
        type = 'ResponseException';
        message = '服务端错误,状态码:${err.response?.statusCode}';
        break;
      case DioExceptionType.cancel:
        level = ExceptionLevel.info;
        type = 'CancelException';
        message = '请求被取消';
        break;
      default:
        level = ExceptionLevel.warning;
        type = 'UnknownException';
        message = '未知异常';
        break;
    }

    // 处理异常
    _handleException(
      exception: err.error ?? err,
      requestOptions: err.requestOptions,
      level: level,
      type: type,
      message: '$message:${err.message}',
    );

    handler.next(err);
  }

  // 统一处理异常:日志记录+远程上报
  void _handleException({
    required dynamic exception,
    required RequestOptions requestOptions,
    required ExceptionLevel level,
    required String type,
    String? message,
  }) {
    // 1. 构建异常模型
    final requestInfo = RequestInfo(
      method: requestOptions.method,
      path: requestOptions.path,
      url: requestOptions.uri.toString(),
      queryParams: requestOptions.queryParameters,
      requestData: requestOptions.data,
      headers: requestOptions.headers,
      timeout: requestOptions.connectTimeout?.inMilliseconds,
    );

    final networkException = NetworkException(
      exceptionId: generateExceptionId(),
      level: level,
      type: type,
      message: message ?? exception.toString(),
      requestInfo: requestInfo,
      timestamp: DateTime.now().millisecondsSinceEpoch,
      extra: {
        'stackTrace': exception is Error ? exception.stackTrace.toString() : '',
      },
    );

    // 2. 本地日志记录
    if (config.enableLocalLog) {
      _logException(networkException);
    }

    // 3. 远程上报(仅上报级别大于等于阈值的异常)
    if (config.enableRemoteReport && _shouldReport(level)) {
      _reportException(networkException);
    }
  }

  // 本地日志记录(调试环境打印,生产环境可写入本地文件)
  void _logException(NetworkException exception) {
    if (kDebugMode) {
      print('\n===== 网络异常日志 =====');
      print('异常ID:${exception.exceptionId}');
      print('级别:${exception.level.name}');
      print('类型:${exception.type}');
      print('消息:${exception.message}');
      print('请求信息:${json.encode(exception.requestInfo.toJson(), indent: 2)}');
      print('额外信息:${json.encode(exception.extra, indent: 2)}');
      print('======================\n');
    }
    // 生产环境可在此处将日志写入本地文件,便于后续排查
  }

  // 远程上报异常
  Future<void> _reportException(NetworkException exception) async {
    if (config.onReport != null) {
      try {
        await config.onReport!(exception);
      } catch (e) {
        if (kDebugMode) {
          print('异常上报失败:$e');
        }
      }
    }
  }

  // 判断是否需要上报(级别大于等于阈值)
  bool _shouldReport(ExceptionLevel level) {
    return level.index >= config.reportLevelThreshold.index;
  }
}

3. 在NetworkUtil中整合所有进阶功能

将加密拦截器、并发控制拦截器、异常监控拦截器整合到NetworkUtil中,形成完整的高级网络请求体系,并提供统一的请求封装方法。


import 'package:dio/dio.dart';
import 'encrypt_interceptor.dart';
import 'concurrent_control_interceptor.dart';
import 'exception_monitor_interceptor.dart';
import 'encrypt_util.dart';

class AdvancedNetworkUtil {
  static final AdvancedNetworkUtil _instance = AdvancedNetworkUtil._internal();
  factory AdvancedNetworkUtil() => _instance;
  late Dio _dio;
  late EncryptUtil _encryptUtil;
  late ConcurrentControlInterceptor _concurrentInterceptor;

  AdvancedNetworkUtil._internal() {
    _encryptUtil = EncryptUtil();
    _initDio();
  }

  // 初始化Dio(整合所有拦截器)
  void _initDio() {
    _dio = Dio();

    // 1. 初始化加密工具(实际项目中,公钥从服务端获取,私钥本地安全存储)
    _encryptUtil.initRSAKeyPair(
      publicKeyStr: '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----',
      privateKeyStr: '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----',
    );

    // 2. 添加异常监控拦截器(最先添加,捕获所有环节的异常)
    _dio.interceptors.add(
      ExceptionMonitorInterceptor(
        config: ExceptionMonitorConfig(
          enableLocalLog: true,
          enableRemoteReport: true,
          reportLevelThreshold: ExceptionLevel.warning,
          onReport: (exception) async {
            // 实现远程上报逻辑(如POST到服务端的异常监控接口)
            await _dio.post(
              'monitor/network/exception',
              data: exception.toJson(),
              options: Options(extra: {'concurrentControl': ConcurrentControlConfig(skipLimit: true)}),
            );
          },
        ),
      ),
    );

    // 3. 添加并发控制拦截器
    _concurrentInterceptor = ConcurrentControlInterceptor(defaultEnableLimit: true);
    _dio.interceptors.add(_concurrentInterceptor);

    // 4. 添加加密拦截器
    _dio.interceptors.add(
      EncryptInterceptor(
        config: EncryptInterceptorConfig(
          enableEncrypt: true,
          signSecret: 'your_sign_secret', // 与服务端约定的签名密钥
          skipEncryptPaths: ['/api/login'], // 登录接口跳过加密
        ),
      ),
    );

    // 5. 其他基础配置(如基础URL、超时时间默认值)
    _dio.options.baseUrl = 'https://api.your-domain.com';
    _dio.options.connectTimeout = const Duration(milliseconds: 15000);
  }

  // 动态调整并发控制速率(如弱网时降低速率)
  void adjustConcurrentRate(double newRate) {
    _concurrentInterceptor.adjustTokenRate(newRate);
  }

  // 动态开关加密功能
  void toggleEncrypt(bool enable) {
    final encryptInterceptor = _dio.interceptors.firstWhere(
      (interceptor) => interceptor is EncryptInterceptor,
    ) as EncryptInterceptor;
    encryptInterceptor.config = encryptInterceptor.config.copyWith(enableEncrypt: enable);
  }

  // 封装带进阶功能的请求方法
  Future<T?> request<T>(
    String path, {
    required String method,
    Map<String, dynamic>? queryParams,
    dynamic data,
    Options? options,
    // 并发控制配置
    ConcurrentControlConfig? concurrentControl,
    // 加密配置(是否跳过当前请求的加密)
    bool skipEncrypt = false,
  }) async {
    final extra = <String, dynamic>{};
    if (concurrentControl != null) {
      extra['concurrentControl'] = concurrentControl;
    }
    if (skipEncrypt) {
      extra['skipEncrypt'] = true;
    }

    final requestOptions = Options(
      method: method,
      ...options,
      extra: {
        ...options?.extra ?? {},
        ...extra,
      },
    );

    try {
      final response = await _dio.request(
        path,
        queryParameters: queryParams,
        data: data,
        options: requestOptions,
      );
      return response.data as T?;
    } catch (e) {
      if (kDebugMode) {
        print('请求失败:$e');
      }
      rethrow;
    }
  }

  // 封装GET请求
  Future<T?> get<T>(
    String path, {
    Map<String, dynamic>? queryParams,
    Options? options,
    ConcurrentControlConfig? concurrentControl,
    bool skipEncrypt = false,
  }) =>
      request<T>(
        path,
        method: 'GET',
        queryParams: queryParams,
        options: options,
        concurrentControl: concurrentControl,
        skipEncrypt: skipEncrypt,
      );

  // 封装POST请求
  Future<T?> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParams,
    Options? options,
    ConcurrentControlConfig? concurrentControl,
    bool skipEncrypt = false,
  }) =>
      request<T>(
        path,
        method: 'POST',
        data: data,
        queryParams: queryParams,
        options: options,
        concurrentControl: concurrentControl,
        skipEncrypt: skipEncrypt,
      );
}

final advancedNetUtil = AdvancedNetworkUtil();

五、结语:构建“安全、可控、可追溯”的高级网络体系

Flutter应用的网络请求进阶优化,核心是“安全防护+流量控制+问题追溯”的三位一体。本文从请求全链路加密、基于令牌桶的并发控制,到分级异常监控与上报,形成了覆盖“数据安全-流量可控-问题可追溯”的全链路实战方案,与上一篇的“离线缓存-弱网适配”形成互补,共同构建企业级的稳定网络交互体系。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐