Android及Flutter Web应用访问GitHub Pages时CORS预检请求失败求助
我有一款Android应用和一款Flutter Web应用,需要从GitHub Pages站点获取文件。
通过执行命令确认GitHub.io的CORS策略允许所有源:
.\curl -v https://mygithubaccount.github.io/myrepository/config.yaml > log_command.txt
返回的响应头包含:
< server: GitHub.com < content-type: text/yaml < permissions-policy: interest-cohort=() < last-modified: Thu, 06 Jun 2024 10:05:37 GMT < access-control-allow-origin: *
但Android WebView和浏览器发送的预检(Preflight)请求均失败,启用Web安全时出现错误:
Access to XMLHttpRequest at 'https://mygithubaccount.github.io/myrepository/config.yaml' from origin 'http://localhost:63988' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Android应用中,WebView内的JS执行请求代码:
response = await fetch(urlString, { headers: { 'method': 'GET','Content-Type': 'application/yaml' }, });
捕获到的错误:
TypeError fetch: Failed to fetch
调试时看到的预检请求头:
"Origin" -> "https://www.myapp.info" "Accept" -> "*/*" "Access-Control-Request-Method" -> "GET" "Access-Control-Request-Headers" -> "content-type,method" "User-Agent" -> "Mozilla/5.0 (Linux; Android 11; SM-A105FN Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 myapp_app_mobile_android" "Referer" -> "https://www.myapp.info/" "Sec-Fetch-Mode" -> "cors"
已在Android代码中设置:
cacheWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
并尝试拦截OPTIONS预检请求返回自定义响应:
cacheWebView.setWebViewClient(new WebViewClient(){ @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { Map<String,String> headers=request.getRequestHeaders(); // 检查是否是预检请求(OPTIONS方法) if (headers.get("Access-Control-Request-Method") != null) { // 创建自定义响应跳过预检 InputStream emptyStream = new ByteArrayInputStream(new byte[0]); headers.put("Access-Control-Allow-Origin","*"); headers.put("Access-Control-Allow-Methods", "GET, HEAD"); headers.put("Access-Control-Allow-Headers", "Content-Type, Authorization"); headers.put("Access-Control-Max-Age", "3600"); return new WebResourceResponse("text/plain", "UTF-8",200,"ok",headers, emptyStream); } // 其他请求正常处理 return super.shouldInterceptRequest(view, request); }
但错误依旧,无法获取内容,需要同时解决Android和Flutter Web的问题。
1. 修复前端请求代码(通用解决Android WebView和Flutter Web问题)
问题核心是你的请求额外添加了不必要的method请求头,且指定了Content-Type: application/yaml——这两个操作触发了浏览器的预检OPTIONS请求,而GitHub Pages服务器对OPTIONS请求的响应未正确返回CORS头(仅GET请求返回了允许跨域的头)。
修改fetch代码,去掉多余的method头,且无需手动指定Content-Type(浏览器会自动适配,或GitHub Pages返回的text/yaml可兼容):
response = await fetch(urlString);
若必须指定Content-Type,请改为与服务器返回一致的text/yaml,但更推荐完全去掉该头(GET请求通常不需要设置Content-Type):
response = await fetch(urlString, { headers: { 'Content-Type': 'text/yaml' } });
修改后请求不会触发预检,直接发送GET请求即可获取带access-control-allow-origin: *的响应。
2. 优化Android WebView拦截逻辑(若需保留触发预检的请求头)
若因业务需求必须保留触发预检的请求头,需修正拦截逻辑:
- 原代码仅检查
Access-Control-Request-Method头,未判断请求方法是否为OPTIONS,存在误判风险 - 自定义响应时应设置响应头,而非修改请求头后返回,原代码的
headers.put操作错误
修正后的拦截代码:
cacheWebView.setWebViewClient(new WebViewClient(){ @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { // 明确判断是OPTIONS方法的预检请求 if (request.getMethod().equalsIgnoreCase("OPTIONS")) { Map<String, String> responseHeaders = new HashMap<>(); responseHeaders.put("Access-Control-Allow-Origin", "*"); responseHeaders.put("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS"); responseHeaders.put("Access-Control-Allow-Headers", "Content-Type, method"); // 匹配请求中的头字段 responseHeaders.put("Access-Control-Max-Age", "3600"); InputStream emptyStream = new ByteArrayInputStream(new byte[0]); return new WebResourceResponse("text/plain", "UTF-8", 200, "OK", responseHeaders, emptyStream); } return super.shouldInterceptRequest(view, request); } });
同时确保WebView启用JavaScript:
cacheWebView.getSettings().setJavaScriptEnabled(true);
3. Flutter Web额外处理
Flutter Web的CORS问题同样通过修改请求代码解决,去掉不必要的请求头避免触发预检:
import 'dart:convert'; import 'package:http/http.dart' as http; Future<String> fetchConfig() async { final response = await http.get( Uri.parse('https://mygithubaccount.github.io/myrepository/config.yaml'), // 不要添加多余的headers,尤其是Content-Type或method字段 ); if (response.statusCode == 200) { return response.body; } else { throw Exception('Failed to load config'); } }
若必须设置请求头,仅保留必要字段,避免触发预检。
内容的提问来源于stack exchange,提问作者P5music

