如何配置 traefik 来处理 CORS 预检请求?

Haj*_*zip 1 cors preflight docker traefik fastapi

我有一个非常简单的 FastAPI-Traefik-Docker 设置,您可以在这里找到: https: //github.com/mshajarrazip/fastapi-traefik-docker-cors

\n

只需docker-compose up -d运行 FastAPI 和 traefik 服务即可。

\n

此设置中未配置 TLS/SSL。

\n

该应用程序有两个端点 GET“/hello”和 POST“/add”,它们除了返回 json 响应之外什么也不做:

\n

app/main.py

\n
import logging\n\nfrom app import routes\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\ndef create_application() -> FastAPI:\n    app = FastAPI(title="\xe2\x9c\xa8 ML API \xe2\x9c\xa8")\n\n    app.include_router(routes.hello.router)\n\n    # add CORS\n    # origins = ["*"]\n    app.add_middleware(\n        CORSMiddleware,\n        allow_origins=["*"], # Allows all origins\n        allow_credentials=True,\n        allow_methods=["*"], # Allows all methods\n        allow_headers=["*"], # Allows all headers\n        )\n\n    return app\n\n\napp = create_application()\n
Run Code Online (Sandbox Code Playgroud)\n

app/routes/...

\n
from fastapi import APIRouter\n\nrouter = APIRouter()\n\n@router.get("/hello")\ndef hello():\n    return {\n        "message": "Hello!"\n    }\n\n@router.post("/add")\ndef add_to_db():\n    return {\n        "id": 1, \n        "name": "Salmah",\n        "age": 29\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

通过curl 和Postman 调用这些端点是有效的:

\n
curl -X \'GET\' http://172.16.239.132:81/hello -H \'Host: basic.api\'\ncurl -X \'POST\' http://172.16.239.132:81/add -H \'Host: basic.api\'\n
Run Code Online (Sandbox Code Playgroud)\n

但不是从 Chrome 浏览器上运行的简单 html 文件中的简单 javascript 获取(从我的本地计算机到运行 traefik 的远程服务器):

\n
<html>\n    <Button id="btnAddData" onclick="AddData();"> Add Data </Button>\n</html>\n\n<script>\nvar myHeaders = new Headers();\nmyHeaders.append("Content-Type", "application/json");\nmyHeaders.append("Host", "basic.api");\n\nvar requestOptions = {\n  method: \'POST\',\n  headers: myHeaders,\n  redirect: \'follow\'\n};\n\nfunction AddData(){\n  console.log("click");\n\n  fetch("http://172.16.239.132:81/add", requestOptions)\n  .then(response => response.text())\n  .then(result => console.log(result))\n  .catch(error => console.log(\'error\', error));\n}\n</script>\n
Run Code Online (Sandbox Code Playgroud)\n

回复:

\n
Access to fetch at \'http://172.16.239.132:81/add\' from origin \'null\' 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. If an opaque response serves your needs, set the request\'s mode to \'no-cors\' to fetch the resource with CORS disabled.\n
Run Code Online (Sandbox Code Playgroud)\n

traefik 通过以下方式记录docker-compose logs -f reverse-proxy

\n
reverse-proxy_1  | 10.32.26.13 - - [18/Dec/2022:03:34:45 +0000] "OPTIONS /add HTTP/1.1"  404 19 "-" "-" 9 "-" "-" 0ms\n
Run Code Online (Sandbox Code Playgroud)\n

我认为问题可能是因为我正在从本地文件系统运行 fetch,所以我使用curl 模拟 OPTIONS 请求(这是请求 GET):

\n
curl -i -X OPTIONS \\\n    -H \'Access-Control-Request-Method: GET\' \\\n    -H \'Access-Control-Request-Headers: Content-Type\' \\\n    -H \'Host: basic.api\' \\\n    "http://172.16.239.132:81/hello"\n
Run Code Online (Sandbox Code Playgroud)\n

我收到 405 Method not allowed 错误:

\n
HTTP/1.1 405 Method Not Allowed\nAllow: GET\nContent-Length: 31\nContent-Type: application/json\nDate: Sun, 18 Dec 2022 03:39:40 GMT\nServer: uvicorn\n\n{"detail":"Method Not Allowed"}\n
Run Code Online (Sandbox Code Playgroud)\n

当我设置时也是如此\'Access-Control-Request-Method: OPTIONS\'

\n

这是我在 docker-compose.yml 中的 traefik 设置:

\n
services:\n  reverse-proxy:\n    image: traefik:v2.9\n    ports:\n      - ${API_PORT}:80\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n      - ./traefik.yml:/etc/traefik/traefik.yml\n    labels:\n      - "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=*"\n      - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=*"\n      - "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"\n      - "traefik.http.middlewares.cors.headers.addvaryheader=true"\n
Run Code Online (Sandbox Code Playgroud)\n

我遵循了关于CORS headers 的官方 Traefik 文档的建议,这显然不是我需要知道的全部内容。

\n

我不是 CORS/网络专家,因此这增加了我当前的困难:(我不知道如何解决这个问题。我已经用各种解决方案组合研究了五天。在首先,我怀疑 FastAPI 的 CORSMiddleware 模块有问题 - 但 FastAPI 代码中设置的 CORSMiddleware 正在工作,因为我在没有 traefik 的情况下对其进行了测试,并且工作正常。

\n

我确信这是 traefik 新手中的一个常见问题,但在我用谷歌搜索的所有地方,我都找不到简单的答案(或者也许答案已经存在,我只是不明白)。另外,他们的所有设置并不像我当前的设置那么简单(他们配置了 TLS/SSL 等一切)。

\n

我认为问题不在于 FastAPI,而在于 traefik 本身。当通过 fetch 调用端点时,我认为请求甚至不会到达 FastAPI 应用程序。因此,任何解决方案都应该适用于 traefik 反向代理,因此,应该适用于任何其他部署,无论它是否是 FastAPI。

\n

请帮助:(我相信它也会帮助很多初学者。

\n

Ami*_*rov 5

要在 docker-compose.yml 文件中为后端服务启用 CORS,您可以向后端服务添加以下标签:

  # Enable CORS headers
  - "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=*"
  - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=*"
  - "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"
  - "traefik.http.middlewares.cors.headers.addvaryheader=true"
Run Code Online (Sandbox Code Playgroud)

这些标签将配置 CORS 中间件以允许任何 HTTP 方法、任何来源以及 CORS 标头的最长期限为 100 秒。addvaryheader 标志还将向响应添加 Vary 标头,以指示响应可能会根据 Origin 标头而有所不同。

将这些标签添加到后端服务后,您可以通过将中间件添加到路由器的中间件标签来将 CORS 中间件应用到路由器。例如:

  # Use the CORS middleware for the app-https router
  - traefik.http.routers.app-https.middlewares=cors
Run Code Online (Sandbox Code Playgroud)

这会将 CORS 中间件应用于 app-https 路由器,允许后端服务接受来自指定来源的 CORS 请求。就我而言,它是 https。然而; 我认为你应该为 http 添加这个

  - traefik.http.routers.my-router.middlewares=cors
Run Code Online (Sandbox Code Playgroud)