Pydantic assemble_cors_origins函数在FastAPI CORS配置中失效排查
问题分析与解决
核心原因
问题出在Pydantic AnyHttpUrl对象转字符串后的格式差异:
当你通过settings.BACKEND_CORS_ORIGINS获取的是AnyHttpUrl实例列表时,将其转为字符串会自动补充末尾的斜杠(比如http://localhost:3000会变成http://localhost:3000/),而前端请求的Origin头通常不带末尾斜杠。FastAPI的CORSMiddleware会严格匹配origin字符串,两者不相等时会拒绝预检请求,导致出现Response to preflight request doesn't pass access control check: It does not have HTTP ok status错误。
而你用ast.literal_eval直接读取环境变量的字符串列表时,这些字符串和前端的Origin头完全一致,所以匹配正常。
验证方法
可以在代码中打印两种方式的origin值对比,确认格式差异:
# 打印settings转后的字符串 print([str(origin) for origin in settings.BACKEND_CORS_ORIGINS]) # 打印ast解析的结果 print(ast.literal_eval(os.getenv("BACKEND_CORS_ORIGINS", "[]")))
解决办法
在将AnyHttpUrl转为字符串时,去除末尾的斜杠,保证和请求头格式一致:
if settings.BACKEND_CORS_ORIGINS: # 去除每个origin末尾的斜杠 BACKEND_CORS_ORIGINS = [str(origin).rstrip("/") for origin in settings.BACKEND_CORS_ORIGINS] app.add_middleware( CORSMiddleware, allow_origins=BACKEND_CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
额外优化(可选)
如果希望从根源避免这个问题,可以修改config.py中的验证器,在解析origin时就标准化格式:
from pydantic import AnyHttpUrl, field_validator from typing import Union, List import json BACKEND_CORS_ORIGINS: List[AnyHttpUrl] @field_validator("BACKEND_CORS_ORIGINS", mode="before") @classmethod def assemble_cors_origins(cls, value: Union[str, List[str]]) -> Union[List[str], str]: backend_cors_origins = None if isinstance(value, str) and not value.startswith("["): backend_cors_origins = [i.strip().rstrip("/") for i in value.split(",")] elif isinstance(value, (list, str)): if isinstance(value, list): backend_cors_origins = [v.rstrip("/") for v in value] else: # 解析JSON字符串并处理斜杠 backend_cors_origins = [v.rstrip("/") for v in json.loads(value)] if backend_cors_origins: return backend_cors_origins raise ValueError(value)
内容的提问来源于stack exchange,提问作者Javier Martín Pizarro
相关产品推荐
相关产品推荐

