You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:34:59