我有一个带下载端点的 FastAPI 应用程序。此下载端点的作用是使用
BlobServiceClient
(对于 Azure Blob 存储)生成请求中指定的文件的令牌和 Blob URL。我想要做的是将用户重定向到该 URL。这是下载点的代码片段(我注释掉了一些内容,因为我不允许显示代码)。
@router.get("..path", tags=["some tags"], summary=..., responses={404: {"model": ...}, 403: {"model": ...}, 307: {"model": ...}}, response_model_exclude_none=True)
async def download_file(
# there's a depends on an API key
blob_path: str = Query(
...
)):
credential = ClientSecretCredential(...) //secrets
blob_service_client = BlobServiceClient(f"https://{storage_account}.blob.core.windows.net", credential=credential)
user_delegation_key = blob_service_client.get_user_delegation_key(key_start_time=datetime.utcnow(),key_expiry_time=datetime.utcnow() + timedelta(minutes=30))
token = generate_blob_sas(account_name=...,
container_name=...,
blob_name=blob_path,
user_delegation_key=user_delegation_key,
permission=BlobSasPermissions(read=True),
expiry=datetime.utcnow() + timedelta(minutes=30))
blob_url = f'https://{storage_account}.blob.core.windows.net/{container_name}/{blob_path}?{token}'
print(blob_url)
response = RedirectResponse(blob_url)
return response
我期望的是执行查询,返回响应后,下载将在后台或单独的选项卡中开始。相反,我得到的是不同的响应,正如您在 Swagger 中看到的那样:
我还查看了“网络”选项卡,看看该请求发生了什么:
看起来有一个
OPTIONS
请求,我假设我正在收到对该请求的响应。不确定 Swagger 是否是这样处理请求的。知道如何/为什么会发生这种情况以及如何解决它吗?谢谢!
首先,CORS中的HTTP OPTIONS是一个由网络浏览器在实际请求之前自动发出的预检请求,而不是返回
File
响应的请求。它请求给定服务器允许的通信选项,服务器使用 Access-Control-Allow-Methods
标头进行响应,其中包含一组允许的方法(例如,Access-Control-Allow-Methods: OPTIONS, GET, HEAD, POST, DELETE
)。可以选择使用 Access-Control-Max-Age
标头为在同一 URL 中创建的请求缓存预检响应,从而允许服务器限制预检请求的数量。该标头的值以秒为单位表示;因此,例如,允许缓存 10 分钟看起来就像 Access-Control-Max-Age: 600
。
RedirectResponse
,Swagger UI 始终遵循重定向响应。例如,在 fetch
请求中,redirect
参数将设置为 follow
,指示应遵循 redirect
。这意味着 Swagger UI 遵循重定向并等待完全收到响应,然后再向您提供允许您下载文件的 Download file
链接(如您上面提供的屏幕截图所示)。这也是为什么您无法在后台或新选项卡中看到下载开始的原因。正如上面链接的 github 帖子中提到的,无法更改该行为,这可以让您以不同的方式处理它,类似于这个答案中演示的方法。
您可以通过在浏览器地址栏中输入 API 端点的 URL 来直接测试它,而不是使用 Swagger UI 来测试该特定端点(因为它是一个
GET
端点,您可以这样做,就像当您在浏览器的地址栏中输入 URL,它会执行 GET
请求),或者创建您自己的自定义 Template(或使用 HTMLResponse
)并提交 HTML <form>
,如图所示在这个答案。