Flutter Dio网络请求最佳实践

637 阅读1分钟

结合前端开发经验,对Dio进行封装。

不是很必要,拦截处理下就行。

主要实现以下功能:

  1. 请求拦截,添加统一请求头
  2. 封装get、post
  3. response解析
  4. response异常处理
  5. token过期处理(结合eventBus)

实现代码如下:

创建文件 customHttp.dart

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

import 'package:flutter/foundation.dart';

import 'CustomEvent.dart';
import 'SharedPreferencesService.dart';
import 'constants.dart';

final options = BaseOptions(
  baseUrl: Constants.serverAddress,//远程服务器地址,即请求前缀
  connectTimeout: const Duration(seconds: 12),
  receiveTimeout: const Duration(seconds: 12),
);
final dio = Dio(options);

class CustomInterceptor extends Interceptor{

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    // print("拦截");
    try{
        //请求头添加token
        String? token = SharedPreferencesService.instance.getString("token");
        if(token!=null)options.headers["token"]=token;
    }catch(e){
      print(e);
    }
    // print('REQUEST[${options.method}] => PATH: ${options.path}');
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (kDebugMode) {
      print('RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
    }

    super.onResponse(response, handler);
  }

  /*@override
  void onError(DioError err, ErrorInterceptorHandler handler) async{
    if (kDebugMode) {
      print('ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    }
    super.onError(err, handler);
  }*/

}

class DioApi{

  static get(String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async{
    try{
      dynamic response = await dio.get(path,
          queryParameters:queryParameters,
          options: options
      );

      return _handleResponse(response);
    }on DioError catch(e){
      return _handleException(e);
    }
  }

  static post(String path, {
      data,
      Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress,
      }) async{
        try{

          dynamic response = await dio.post(path,
              data:data,
              queryParameters:queryParameters,
              options: options
          );
          return _handleResponse(response);
        }on DioError catch(e){
          return _handleException(e);
        }
  }

  static dynamic _handleResponse(Response<dynamic> response){
    Map<String,dynamic> res =jsonDecode(response.toString());


    return res;
  }

  static dynamic _handleException(e) async{

    //异常响应

    String msg = e.message??"message为空";
    int code;
    if (e.response != null) {

      code = e.response.data["code"];

      if(e.response.data["code"]==401){
        msg = "登录已过期,请重新登录";
        final prefs = SharedPreferencesService.instance;
        await prefs.remove("token");

      }
    } else {

      code = 500;
      
    }
    
    // 格式保持和后端返回一致,或自行封装定义
    late Map<String, dynamic> response;
    response = e.response?.data ?? {
      "code": code,
      "msg": msg,
    };

    if(code==401) {
      response={
        "code":code,
        "msg":msg,
      };
      //token过期,触发过期事件
      eventBus.fire(TokenExpireEvent());
    }
    return response;
  }
}

使用

    import 'package:dio/dio.dart';
    import 'customHttp.dart';
    void main() async {  
        // add interceptors  
        dio.interceptors.add(LogInterceptor());  
        dio.interceptors.add(CustomInterceptor());
    }

token过期监听

在入口页面加入监听代码

    // 事件监听  
    Future.delayed(Duration.zero,(){  
        eventBus.on<TokenExpireEvent>().listen((event) {  
            // All events are of type TokenExpireEvent (or subtypes of it).  
            try{  
                context.pushReplacement("/login");  
            }catch(e){  
                if (kDebugMode) {  
                    print(e);  
                }  
            }  
        });  
    });