尽管有效的 CORS 配置,“请求头字段 Access-Control-Allow-Origin 在预检响应中不被 Access-Control-Allow-Headers 允许”

Bri*_*n C 3 javascript python cors

我使用 Google Cloud Functions 创建了一个 API 端点,并尝试从 JS 提取函数中调用它。

我遇到了我很确定与 CORS 或输出格式有关的错误,但我不确定发生了什么。其他一些 SO 问题是类似的,并帮助我意识到我需要删除mode: "no-cors". 大多数提到在 BE 上启用 CORS,所以我添加了response.headers.set('Access-Control-Allow-Origin', '*')- 我在本文中了解到- 以确保启用 CORS...但我仍然收到“无法获取”错误。

完整错误(可在下面链接的实时演示中重现)是:

未捕获的错误:无法添加节点 1,因为具有该 ID 的节点已在 Store 中。(这个应该是无关的吧?

从源“https://o2gxx.csb.app”访问“https://us-central1-stargazr-ncc-2893.cloudfunctions.net/nearest_csc?lat=37.75&lon=-122.5”已被阻止CORS 策略:预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段 access-control-allow-origin。

获取 https://us-central1-stargazr-ncc-2893.cloudfunctions.net/nearest_csc?lat=37.75&lon=-122.5 net::ERR_FAILED

Uncaught (in promise) TypeError: Failed to fetch

请参阅下面的代码片段,请注意我用来<---- *** Message ***表示最近更改的代码部分的位置,这给了我这两个错误之一。

前端代码:

function getCSC() {
  let lat = 37.75;
  let lng = -122.5;

  fetch(
    `https://us-central1-stargazr-ncc-2893.cloudfunctions.net/nearest_csc?lat=${lat}&lon=${lng}`,
    {
      method: "GET",
      // mode: "no-cors", <---- **Uncommenting this predictably gets rid of CORS error but returns a Opaque object which seems to have no data**
      headers: {
        // Accept: "application/json", <---- **Originally BE returned stringified json. Not sure if I should be returning it as something else or if this is still needed**
        Origin: "https://lget3.csb.app",
        "Access-Control-Allow-Origin": "*"
      }
    }
  )
  .then(response => {
      console.log(response);
      console.log(response.json());
    });
}
Run Code Online (Sandbox Code Playgroud)

后端代码:

import json
import math
import os
import flask

def nearest_csc(request):
    """
    args: request object w/ args for lat/lon
    returns: String, either with json representation of nearest site information or an error message
    """

    lat = request.args.get('lat', type = float)
    lon = request.args.get('lon', type = float)

    # Get list of all csc site locations
    with open(file_path, 'r') as f:
        data = json.load(f)
        nearby_csc = []

        # Removed from snippet for clarity:
        #    populate nearby_csc (list) with sites (dictionaries) as elems
        #    Determine which site is the closest, assigned to var 'closest_site'              

        # Grab site url and return site data if within 100 km
        if dist_km < 100:
            closest_site['dist_km'] = dist_km
            
            // return json.dumps(closest_site) <--- **Original return statement. Added 4 lines below in an attempt to get CORS set up, but did not seem to work**

            response = flask.jsonify(closest_site)
            response.headers.set('Access-Control-Allow-Origin', '*')
            response.headers.set('Access-Control-Allow-Methods', 'GET, POST')
            return response

        return "No sites found within 100 km"
Run Code Online (Sandbox Code Playgroud)

上面代码片段的完整上下文:

我还想知道 CodeSandbox 是否有可能以一种奇怪的方式执行 CORS,但是localhost:3000在 .

错误似乎与 CORS 相关 ( 'https://o2gxx.csb.app' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.) 但我认为添加response.headers.set('Access-Control-Allow-Origin', '*')会解决这个问题。我需要更改 BE 上的其他内容吗?在FE上?

TLDR;

即使在尝试在后端启用 CORS 并将标头添加到 FE 之后,我也收到错误“无法获取”和“访问控制允许标头不允许访问控制允许来源”。有关代码的实时演示,请参阅上面的链接。

sid*_*ker 8

删除前端代码中添加 Access-Control-Allow-Origin标题的。

从不添加 Access-Control-Allow-Origin在前端代码中为请求标头。

那这辈子唯一的作用是一负一:它会导致浏览器做CORS预检OPTIONS甚至在案件的请求时,实际的(GETPOST,等),从您的前端代码的要求,否则不会触发预检。然后预检将失败并显示以下消息:

预检响应中的 Access-Control-Allow-Headers 不允许请求标头字段 Access-Control-Allow-Origin

...也就是说,除非向其发出请求的服务器已配置为发送Access-Control-Allow-Headers: Access-Control-Allow-Origin响应标头,否则它将因此失败。

但你永远不希望Access-Control-Allow-OriginAccess-Control-Allow-Headers响应标头值。如果这最终使事情奏效,那么您实际上只是在解决错误的问题。因为真正的解决方法是:永远不要设置Access-Control-Allow-Origin为请求标头。

直观地说,将其视为“我Access-Control-Allow-Origin在请求和响应中都进行了设置,因此应该比仅在响应中设置更好”似乎合乎逻辑——但实际上它比仅在响应中设置更糟糕(出于上述原因)。

所以底线:Access-Control-Allow-Origin只是一个响应头,而不是请求头。您只想在服务器端响应代码中设置它,而不是前端 JavaScript 代码。


问题中的代码也试图添加Origin标题。您也永远不想尝试在前端 JavaScript 代码中设置该标头。

Access-Control-Allow-Origin标头的情况不同,Origin它实际上是一个请求标头——但它是一个完全由浏览器控制的特殊标头,浏览器永远不会允许您的前端 JavaScript 代码设置它。所以永远不要尝试。