Flutter中使用Ferry获取GraphQL响应头的方法求助
问题场景
使用Ferry向GraphQL服务器发起查询时,需要从响应头或Cookie中提取access token和refresh token,但发现OperationResponse没有提供访问响应头的属性,无法直接获取目标信息。
当前代码:
@override Stream<OperationResponse<TData, TVars>> request<TData, TVars>( OperationRequest<TData, TVars> request) { try { Stream<OperationResponse<TData, TVars>> response = _client.request<TData, TVars>(request); response.first.then((requestResponse) { final headers = requestResponse // 此处找不到获取headers的途径 final authorization = headers?['authorization']?.firstOrNull; final cookie = headers?['set-cookie']?.firstOrNull; if (authorization != null) { _authToken = authorization; _securedStorageService.saveAuthToken(authorization); } if (cookie != null) { _refreshToken = cookie; _securedStorageService.saveRefreshToken(cookie); final maxAge = cookie.split(';').firstWhere( (element) => element.contains('Max-Age'), orElse: () => '', ); if (maxAge.isNotEmpty) { final expirationSeconds = maxAge.split('=').last; final expires = DateTime.now().add(Duration(seconds: int.parse(expirationSeconds))); _tokenExpired = expires.isBefore(DateTime.now()); _securedStorageService.saveRefreshTokenExpiration(expires.toIso8601String()); } } return requestResponse; }); return response; } catch (e) { rethrow; // 错误交给上层仓库层处理 } }
解决方案
Ferry的OperationResponse本身不会携带原始HTTP响应头,需要通过自定义Link拦截响应来提取头信息。Link是Ferry处理请求/响应的核心链路,HttpLink会把响应头存在HttpLinkResponseContext中,我们可以在自定义Link里取出这些信息。
步骤1:自定义Header提取Link
创建一个继承自Link的类,在request方法中拦截响应并提取头:
class HeaderExtractionLink extends Link { final SecuredStorageService _securedStorageService; String? _authToken; String? _refreshToken; bool _tokenExpired = false; HeaderExtractionLink(this._securedStorageService); @override Stream<Response> request( Request request, [ NextLink? forward, ]) async* { // 转发请求到下一个Link(通常是HttpLink) final responseStream = forward!(request); await for (final response in responseStream) { // 从响应上下文获取HTTP头 final httpContext = response.context.entry<HttpLinkResponseContext>(); final headers = httpContext?.headers; if (headers != null) { // 提取Authorization头 final authorization = headers['authorization']?.firstOrNull; if (authorization != null) { _authToken = authorization; await _securedStorageService.saveAuthToken(authorization); } // 提取Set-Cookie头 final cookie = headers['set-cookie']?.firstOrNull; if (cookie != null) { _refreshToken = cookie; await _securedStorageService.saveRefreshToken(cookie); // 解析Cookie的Max-Age final maxAge = cookie.split(';').firstWhere( (element) => element.contains('Max-Age'), orElse: () => '', ); if (maxAge.isNotEmpty) { final expirationSeconds = maxAge.split('=').last; final expires = DateTime.now().add(Duration(seconds: int.parse(expirationSeconds))); _tokenExpired = expires.isBefore(DateTime.now()); await _securedStorageService.saveRefreshTokenExpiration(expires.toIso8601String()); } } } // 将响应继续传递下去 yield response; } } }
步骤2:将自定义Link加入Ferry Client链路
初始化Ferry Client时,把自定义Link和HttpLink串联起来:
// 创建HttpLink final httpLink = HttpLink('你的GraphQL服务器地址'); // 创建自定义的Header提取Link final headerExtractionLink = HeaderExtractionLink(securedStorageService); // 初始化Ferry Client,串联链路 final client = Client( link: headerExtractionLink.concat(httpLink), cache: Cache(), );
关键说明
- Ferry的链路是链式处理的,自定义Link会先拦截响应,提取头信息后再把响应传递给后续链路,最终生成
OperationResponse。 HttpLinkResponseContext是HttpLink添加到响应上下文中的对象,里面包含了原始的HTTP响应头、状态码等信息。- 不需要再在原来的
request方法中尝试提取头,自定义Link会自动处理所有请求的响应头。
内容的提问来源于stack exchange,提问作者sneep3r4476
相关产品推荐
相关产品推荐

