如何在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
相关产品推荐
相关产品推荐

