`json.dumps()`和`JSONResponse()`之间的FastAPI差异[重复]

rwqw0loc  于 2023-04-22  发布在  其他
关注(0)|答案(2)|浏览(83)

此问题已在此处有答案

How to return data in JSON format using FastAPI?(1个答案)
20小时前关闭。
我正在探索FastAPI,并在Windows上的Docker桌面上运行它。下面是我在Docker中成功部署的main.py

#main.py
import fastapi
import json
from fastapi.responses import JSONResponse

app = fastapi.FastAPI()

@app.get('/api/get_weights1')
async def get_weights1():
    weights = {'aa': 10, 'bb': 20}
    return json.dumps(weights)

@app.get('/api/get_weights2')
async def get_weights2():
    weights = {'aa': 10, 'bb': 20}
    return JSONResponse(content=weights, status_code=200)

我有一个简单的python文件get_weights.py来向这两个API发出请求:

#get_weights.py
import requests
import json

resp = requests.get('http://127.0.0.1:8000/api/get_weights1')
print('ok', resp.status_code)
if resp.status_code == 200:
    print(resp.json())

resp = requests.get('http://127.0.0.1:8000/api/get_weights2')
print('ok', resp.status_code)
if resp.status_code == 200:
    print(resp.json())

我从两个API得到相同的响应,输出:

ok 200
{"aa": 10, "bb": 20}
ok 200
{'aa': 10, 'bb': 20}

无论我使用json.dumps()还是JSONResponse(),响应似乎都是一样的。我已经阅读了FastAPI documentation on JSONResponse,但我仍然有以下问题:
请问这两种方法有什么不同吗?
如果存在差异,建议使用哪种方法(为什么?)?

0s7z1bwu

0s7z1bwu1#

在FastAPI中,你可以用3种不同的方式创建响应(从最简洁到最灵活):

1

return dict # Or model or ...

docs you linked中,我们可以看到FastAPI会自动将这个dict字符串化,并将其 Package 在JSONResponse中。这种方式最简洁,涵盖了大多数用例。

但是有时候你需要返回自定义头(例如REMOTE-USER=username)或不同的状态码(可能是201 - Created或202 - Accepted)。这种情况下你需要使用JSONResponse

return JSONResponse(content=dict) # Here we need to have dict.

问题是,现在如果我们没有简单的dict,我们必须使用jsonable_encoder(some_model) # -> dict来获取它。所以它有点冗长。对于可用的选项,请查看Starlette文档,因为FastAPI只是重新导出它。
更复杂的例子:

return JSONResponse(content=jsonable_encoder(some_model), status_code=201, headers={"REMOTE-USER": username})

最后,你不需要返回json -你也可以返回csv,html或任何其他类型的文件。这种情况下,我们必须使用Response并指定media_type。同样使用Starlette docs。

return Response('Hello, world!', media_type='text/plain')

总之

请注意,Fastapi文档指出:
当您直接返回响应时,其数据不会被验证、转换(序列化),也不会自动记录。
我们来看看有什么不同:第一个方法与其他FastAPI功能集成良好,因此应该始终是首选。只有当您需要提供自定义头或状态码时才使用第二个选项。最后,只有当您希望返回非json的内容时才使用第三个选项。

mwg9r5ms

mwg9r5ms2#

我尝试了一些变化,发现以下...
(1)这两种方法都不能序列化datetime对象。例如,如果权重为:

weights = {'aa': 10, 'bb': 20, 'date': datetime.date.today()}

那么这两个方法将具有相同的返回状态和错误:
500内部服务器错误
TypeError:date类型的对象不是JSON可序列化的
为了克服这种使用

return json.dumps(weights, default=str)

from fastapi.encoders import jsonable_encoder
#blah
return JSONResponse(content=jsonable_encoder(weights), status_code=200)

(2)我还尝试了按原样返回普通的dict,特别是如果其中有一个datetime对象。正如@Matija所提到的,FastAPI会自动将这个dict字符串化并将其 Package 在响应中。例如:

@app.get('/api/get_weights1')
async def get_weights1():
    weights = {'aa': 10, 'bb': 20, 'date': datetime.date.today()}
    return weights    #<--- this is dict

输出:

ok 200
{"aa": 10, "bb": 20, "date": "2023-04-21"}

(3)正如@Matija所提到的,JSONResponse()方法允许自定义返回的响应。例如,响应状态可以自定义为201(而不是200)。还可以返回不同类型的对象。这可能是使用此方法优于json.dumps()方法的优点。例如:

#main.py
@app.get('/api/get_weights2')
async def get_weights2():
    weights = {'aa': 10, 'bb': 20}
    return JSONResponse(content=weights, status_code=201)    #<--201

#get_weights.py
resp = requests.get('http://127.0.0.1:8000/api/get_weights2')
print('ok', resp.status_code)
if resp.status_code == 201:    #<---201
    print(resp.json())

与之前的输出相同:

ok 200
{'aa': 10, 'bb': 20}

相关问题