Dart http包POST响应缺失Postman中存在的Location等响应头问题
问题描述
核心问题:目前在Dart中发起POST请求时,无法获取到Postman调试同接口时返回的部分响应头,应用需要读取这些头的取值才能正常运行,需要找到获取Dart侧未返回、但Postman中存在的响应头的方法。
日常使用Dart/Flutter的http package基本可以满足所有开发需求,但近期遇到问题:该包在处理POST请求响应时,不会返回部分非标准响应头。当前发起的请求示例如下:
POST https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/Patient?_format=json&_pretty=false Headers: {"Content-Type": "application/fhir+json", "Authorization": "Bearer $BearerToken"} Body: {"resourceType":"Patient","identifier":[{"type":{"coding":[{"system":"http://hl7.org/fhir/sid/us-ssn","code":"SB"}] },"system":"urn:oid:2.16.840.1.113883.4.1","value":"444114567"}],"name":[{"use":"usual","text":"Derrick Lin","family":"Lin","given":["Derrick"]}],"gender":"male","birthDate":"1973-06-03"}
该请求本身执行成功,返回201状态码,已在服务端确认Patient资源已成功创建,但Dart侧拿到的响应头仅包含以下字段:
{ "cache-control": "no-cache,no-store", "content-length": 0, "content-type": "application/fhir+json; charset=utf-8", "expires": -1, "pragma": "no-cache" }
发起请求前已做断点校验,确认所有请求参数完全正确;将完全相同的参数填入Postman发起请求,同样返回201状态码,但可以拿到完整的响应头如下:
{ "Expires": -1, "Location": "Patient/eoc0yXThvv5aQEdz-kjaSWQ3", "Allow-Control-Allow-Headers": "origin, authorization, accept, content-type, x-requested-with, Epic-User-ID, Epic-User-IDType, Epic-Client-ID, soapaction, Epic-MyChartUser-ID, Epic-MyChartUser-IDType", "Allow-Control-Allow-Methods": "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS", "Allow-Control-Allow-Origin": "*", "Allow-Control-Allow-Credentials": true, "cache-control": "no-cache,no-store", "content-length": 0, "content-type": "application/fhir+json; charset=utf-8", "pragma": "no-cache" }
需要在Dart中获取这些额外的响应头,尤其是Location头。
现有请求实现逻辑
请求构造流程:首先走OAuth2流程获取Bearer token,之后通过继承http.Client的自定义类调用POST方法,相关实现代码如下:
@override Future<http.Response> post(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async => await http.post( url, headers: await newHeaders(headers), body: body, encoding: encoding, ); @override Future<Map<String, String>> newHeaders(Map<String, String>? headers) async { headers ??= <String, String>{}; if (client?.credentials.accessToken != null) { headers['Authorization'] = 'Bearer ${client!.credentials.accessToken}'; } headers.addAll(authHeaders ?? <String, String>{}); return headers; }
当前请求确实执行成功,资源正常提交创建,返回201状态码,但始终无法拿到完整的响应头,需要定位问题是由请求构造方式错误、http包本身缺陷,还是Dart SDK的问题导致。
问题原因与解决方案
该问题与请求构造错误、http包缺陷、Dart SDK问题均无直接关系,核心原因分两类运行场景:
- Flutter Web运行场景
浏览器CORS同源策略默认仅暴露少量安全响应头给上层代码,你当前能拿到的cache-control、content-length、content-type、expires、pragma都在默认暴露白名单内。需要的Location头及其他自定义响应头不在默认暴露列表中,必须由服务端在响应中添加Access-Control-Expose-Headers字段,明确声明允许前端读取的头字段,浏览器才会将这些头传递给Dart层代码。
Postman作为独立接口调试工具,不受浏览器CORS规则限制,因此可以拿到全部响应头。
处理方式:联系接口提供方,在跨域响应中添加Access-Control-Expose-Headers配置,将Location及其他需要读取的头加入暴露列表,示例:Access-Control-Expose-Headers: Location, Allow-Control-Allow-Headers, Allow-Control-Allow-Methods, Allow-Control-Allow-Origin, Allow-Control-Allow-Credentials - 移动端/桌面端原生运行场景
你当前自定义Client的写法不规范:重写post方法时直接调用了顶层http.post函数,该函数会内部创建独立的临时Client发送请求,不会继承自定义Client的完整处理逻辑,少数场景下会出现响应头解析不全的问题。
处理方式:不要调用顶层http.post方法,改为通过父类send方法构造、发送请求,修正后的代码示例:
注意http包的响应头是大小写不敏感的,读取时不需要严格匹配Postman中的字段大小写,直接通过@override Future<http.Response> post(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async { final request = http.Request('POST', url); request.headers.addAll(await newHeaders(headers)); if (body != null) { request.body = body is String ? body : jsonEncode(body); } if (encoding != null) { request.encoding = encoding; } final streamedResponse = await send(request); return http.Response.fromStream(streamedResponse); }response.headers['location']即可读取对应值。
内容的提问来源于stack exchange,提问作者Grey
相关产品推荐
相关产品推荐

