You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在multipart请求中结合http包使用http_interceptor实现token刷新与重试

实现Multipart请求的令牌刷新拦截器

要在Flutter中实现针对Multipart请求的令牌过期自动刷新并重试,可按以下步骤操作:

1. 添加依赖

在pubspec.yaml中引入http_interceptor包:

dependencies:
  http_interceptor: ^2.0.0 # 替换为最新版本

2. 自定义认证拦截器

创建拦截器类,处理401错误、刷新令牌并重试请求:

import 'package:http/http.dart' as http;
import 'package:http_interceptor/http_interceptor.dart';
import 'dart:convert';
import 'package:your_project_name/api/ninecabsapi.dart'; // 替换为你的API类路径

class AuthInterceptor extends InterceptorContract {
  String? _currentToken;
  final String _refreshToken; // 假设你已存储刷新令牌

  AuthInterceptor(this._currentToken, this._refreshToken);

  @override
  Future<RequestData> interceptRequest({required RequestData data}) async {
    // 为请求添加Authorization头
    if (_currentToken != null) {
      data.headers['Authorization'] = _currentToken!;
    }
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({required ResponseData data}) async {
    // 捕获401未授权状态码
    if (data.statusCode == 401) {
      final newToken = await _fetchNewToken();
      if (newToken != null) {
        _currentToken = newToken;
        // 重试原请求
        return await _retryOriginalRequest(data.requestData, newToken);
      } else {
        // 刷新失败,跳转到登录页
        // Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => LoginPage()));
      }
    }
    return data;
  }

  // 调用刷新令牌接口
  Future<String?> _fetchNewToken() async {
    try {
      final response = await http.post(
        Uri.parse('${Ninecabsapi().urlHost}/refresh-token'),
        headers: {'Authorization': 'Refresh $_refreshToken'},
      );
      if (response.statusCode == 200) {
        final result = jsonDecode(response.body);
        return result['access_token'];
      }
    } catch (e) {
      print('刷新令牌失败: $e');
    }
    return null;
  }

  // 重试请求,区分普通请求和Multipart请求
  Future<ResponseData> _retryOriginalRequest(RequestData originalRequest, String newToken) async {
    if (originalRequest is MultipartRequestData) {
      // 重新构建Multipart请求
      final retryRequest = http.MultipartRequest(
        originalRequest.method,
        Uri.parse(originalRequest.url),
      )
        ..files.addAll(originalRequest.files)
        ..fields.addAll(originalRequest.fields)
        ..headers['Authorization'] = newToken;

      final streamedResponse = await retryRequest.send();
      final response = await http.Response.fromStream(streamedResponse);

      return ResponseData(
        data: response.body,
        statusCode: response.statusCode,
        headers: response.headers,
        requestData: originalRequest,
      );
    } else {
      // 普通请求重试逻辑
      final client = http.Client();
      final retryRequest = http.Request(
        originalRequest.method,
        Uri.parse(originalRequest.url),
      )
        ..headers.addAll(originalRequest.headers)
        ..headers['Authorization'] = newToken
        ..body = originalRequest.body;

      final streamedResponse = await client.send(retryRequest);
      final response = await http.Response.fromStream(streamedResponse);

      return ResponseData(
        data: response.body,
        statusCode: response.statusCode,
        headers: response.headers,
        requestData: originalRequest,
      );
    }
  }
}

3. 改造Multipart请求方法

使用带拦截器的客户端发送请求:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
import 'package:http_interceptor/http_interceptor.dart';
import 'dart:convert';
import 'package:your_project_name/providers/flutter_functions.dart'; // 替换为你的Provider路径
import 'package:your_project_name/api/ninecabsapi.dart';

Future<void> postImage(BuildContext context, String imageType) async {
  var data = {"imagetype": imageType, "filename": imageType};
  Map<String, String> obj = {"attributes": json.encode(data).toString()};
  var flutterFunctions = Provider.of<FlutterFunctions>(context, listen: false);
  final url = Ninecabsapi().urlHost + Ninecabsapi().getvehicle;

  // 从本地存储获取当前令牌和刷新令牌(示例,替换为你的获取逻辑)
  final currentToken = token!;
  final refreshToken = await _getRefreshToken(); // 自定义方法获取刷新令牌

  // 初始化带拦截器的客户端
  final interceptorClient = InterceptorClient(
    interceptors: [AuthInterceptor(currentToken, refreshToken)],
  );

  try {
    loading();
    // 构建Multipart请求
    final multipartRequest = http.MultipartRequest("POST", Uri.parse(url))
      ..files.add(await http.MultipartFile.fromPath(
          "imagefile", flutterFunctions.imageFile!.path,
          contentType: http.MediaType("image", "jpg")))
      ..fields.addAll(obj);

    // 使用拦截器客户端发送请求
    final streamedResponse = await interceptorClient.sendMultipartRequest(multipartRequest);
    final response = await http.Response.fromStream(streamedResponse);

    // 根据响应状态码处理结果
    if (response.statusCode == 200) {
      // 请求成功,处理返回数据
      print('图片上传成功');
    } else {
      print('图片上传失败,状态码: ${response.statusCode}');
    }

    loading();
    notifyListeners();
  } catch (e) {
    print('请求异常: $e');
  }
}

// 示例:获取刷新令牌的方法
Future<String> _getRefreshToken() async {
  // 从SharedPreferences或其他存储中获取
  // SharedPreferences prefs = await SharedPreferences.getInstance();
  // return prefs.getString('refresh_token') ?? '';
  return '';
}

注意事项

  • 并发请求处理:如果多个请求同时触发401,要避免多次调用刷新接口,可以添加锁机制(比如使用Mutex)。
  • 令牌持久化:刷新后的新令牌要及时更新本地存储,确保后续请求使用最新令牌。
  • 错误边界:完善刷新失败后的处理逻辑,比如清除本地令牌并跳转登录页。

内容的提问来源于stack exchange,提问作者Santosh Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 11:48:59