Flutter Web使用http包发起请求因CORS报错的解决方案
问题背景
当前使用GitHub Codespaces进行开发,平台自带本地托管应用的端口转发能力。执行flutter run -d web-server --web-hostname 127.0.0. --web-port命令启动服务时,本地localhost地址会被映射为githubpreview.dev域名的公网可访问地址。
客户端侧编写了数据库连通性测试函数,代码如下:
static Future<bool> testConnection() async { http.Response response = await _httpClient.get( Uri.parse(baseUrl + 'test'), // requestObject.slug.string headers: { HttpHeaders.authorizationHeader: 'Bearer 69', "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale", "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS" }, );
其中_httpClient是对HttpClient()的封装。
服务端对应处理函数代码如下:
export function use_test(request) { console.log(`test request:${request.headers.origin}`); let options = { "headers":{ "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale", "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS" }, "body": { "payload": { "msg": request['headers'] } } }; return ok(options); }
故障现象
每次调用testConnection方法时,请求能正常到达服务端(服务端可打印请求日志),但客户端收不到响应,抛出XMLHttpRequestError错误。开发Flutter Web时多次遇到同类问题,本次一直无法定位。使用Postman发起相同请求可以正常拿到返回数据,初步判断是CORS跨域机制导致的故障。
补充信息
Flutter doctor执行输出如下:
[✓] Flutter (Channel master, 3.1.0-0.0.pre.1354, on Debian GNU/Linux 11 (bullseye) 5.4.0-1074-azure, locale en_US.UTF-8) • Flutter version 3.1.0-0.0.pre.1354 on channel master at /workspaces/Lighthouse-Web/flutter • 上游仓库为Flutter官方代码仓库 • Framework revision a30012b275 (3 days ago), 2022-06-22 17:04:07 -0700 • Engine revision fc08bf45b0 • Dart version 2.18.0 (build 2.18.0-216.0.dev) • DevTools version 2.14.0 [✗] Android toolchain - develop for Android devices ✗ Unable to locate Android SDK. 安装Android Studio后首次启动会自动引导安装Android SDK组件。 若已将Android SDK安装到自定义路径,执行`flutter config --android-sdk`更新对应路径即可。 [✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome) ! 无法找到Chrome程序,可设置CHROME_EXECUTABLE环境变量指定Chrome可执行文件路径。 [✗] Linux toolchain - develop for Linux desktop ✗ 进行Linux桌面端开发需要安装clang++,可通过系统包管理器安装 ✗ 进行Linux桌面端开发需要安装CMake,可通过系统包管理器或官方渠道下载 ✗ 进行Linux桌面端开发需要安装ninja,可通过系统包管理器或官方渠道下载 ✗ 进行Linux桌面端开发需要安装pkg-config,可通过系统包管理器安装 [!] Android Studio (not installed) • 未检测到Android Studio安装,可下载安装后完成对应配置。 [✓] Connected device (1 available) • Linux (desktop) • linux • linux-x64 • Debian GNU/Linux 11 (bullseye) 5.4.0-1074-azure [✓] HTTP Host Availability • 所有需要访问的HTTP主机均可达 ! Doctor found issues in 4 categories.
排查修复方案
按优先级从高到低逐个修改即可解决问题:
- 立刻删掉客户端请求头里所有
Access-Control-*开头的字段。这些字段是服务端返回给浏览器的响应头,放在请求里没有任何作用,反而会因为新增非标准自定义头,强制浏览器在发送正式GET请求前先发起OPTIONS预检请求,平白增加跨域校验环节。 - 修复服务端CORS配置的硬性冲突:CORS规范明确禁止同时设置
Access-Control-Allow-Origin: *和Access-Control-Allow-Credentials: true,二者完全互斥。不需要传递Cookie、TLS客户端凭证这类认证信息的话,直接删掉Access-Control-Allow-Credentials: true配置即可;如果需要传递凭证,就把Access-Control-Allow-Origin的值设置为请求实际的Origin值(也就是你服务端日志打印的request.headers.origin内容),绝对不能用通配符。 - 给服务端补充OPTIONS预检请求的处理逻辑。当前你的服务端只处理了GET方法的
/test路由,浏览器发出的OPTIONS预检请求没有对应处理逻辑,要么返回4xx错误,要么返回的响应没带正确CORS头,浏览器会直接判定跨域失败,拦截后续所有响应。处理逻辑非常简单:所有OPTIONS方法的请求直接返回204状态码,带上和GET响应一致的CORS头即可,不需要返回任何业务内容。 - 修正Flutter启动命令:你当前写的
--web-hostname 127.0.0.参数有误,漏了最后一位地址段,且在Codespaces环境下要让端口转发正常生效,必须绑定0.0.0.0地址,参考正确启动命令:flutter run -d web-server --web-hostname 0.0.0.0 --web-port 自定义端口号。 - 检查Codespaces端口转发配置:在Codespaces的端口管理面板中,找到后端服务对应的转发端口,将可见性设置为公开,同时确认代码里
baseUrl配置的地址和端口转发生成的访问地址完全匹配,不要出现前端跑在githubpreview.dev域名、baseUrl却写localhost的情况。
注意:Postman能正常请求不代表CORS配置正确。Postman作为接口调试工具,本身不受浏览器CORS安全规则限制,不会自动发送预检请求,也不会拦截跨域响应,完全不能作为CORS配置的校验依据。
内容的提问来源于stack exchange,提问作者Obsidian418
相关产品推荐
相关产品推荐

