13.FastAPI Response对象
在某些情况下,可能会需要对请求返回特定的数据,而不是采用默认的方式,此时,就可以直接使用Response对象。在FastAPI中,可以在路由操作函数声明一个fastapi.Response类型的参数,然后在函数的代码直接操作Response对象。
13.1更改响应状态码
可以在路由操作函数中,通过对Response对象的 status_code 赋值来修改响应状态码,代码如下:
from fastapi import FastAPI from fastapi import Response from fastapi import status app = FastAPI() @app.get(path='/hello', status_code=status.HTTP_200_OK) async def hello(response: Response): response.status_code = status.HTTP_201_CREATED return {'hello': 'world'}
执行请求后后台日志输出:
127.0.0.1:50876 - "GET /hello HTTP/1.1" 201 Created
在上面的代码中,虽然在装饰器中使用参数设置为返回状态码为200,但在函数中可以通过Response对象对返回状态码进行修改。
13.2JSON编码器
在某些情况下,可能需要在 Pydantic模型与Python类型之间进行转换,目的是将数据转换为与JSON兼容的数据结构,比如:list、dict等。FastAPI提供 jsonable_encoder()函数来支持这样的数据转换。该函数接受一个Pydantic模型对象作为参数,返回一个对应的JSON兼容版本。
代码如下:
from fastapi import FastAPI from pydantic import BaseModel from datetime import datetime from datetime import time from fastapi.encoders import jsonable_encoder app = FastAPI() class Task(BaseModel): name: str created: datetime runat: time creator: str @app.get(path='/test') async def test(): task = Task(name='hello', created=datetime.now(), runat=time(hour=2,minute=30,second=0), creator='admin') print(type(task)) enjson = jsonable_encoder(task) print(type(enjson)) print(enjson) return task
执行请求:
curl http://127.0.0.1:8000/test { "name":"hello", "created":"2022-01-15T09:17:16.061638", "runat":"02:30:00", "creator":"admin" }
后台print输出:
<class 'main2.Task'> <class 'dict'> {'name': 'hello', 'created': '2022-01-15T09:17:16.061638', 'runat': '02:30:00', 'creator': 'admin'}
从上面的代码及代码执行结果可以看出: 代码将 Pydantic 模型转换为一个字典,其值与JSON兼容,并将这个datetime和time转换为一个字符串。
事实上,在FastAPI内部是使用jsonable_encoder来转换数据的。
13.3 直接返回Response对象
13.3.1 JSONResponse
在前面的代码中,当创建一个FastAPI路由时,可以返回dict、list、Pydantic 模型等,那么FastAPI内部是如何处理的呢?FastAPI默认会使用 jsonable_encoder 将返回值转换为JSON格式, 然后,FastAPI 会在后台将这些兼容 JSON 的数据(比如字典)放到一个 JSONResponse 中,该 JSONResponse 会用来发送响应给客户端。
JSONResponse 是Response 的子类,Response 还有很多子类,在后面的内容中介绍。
13.3.2返回 Response
实际上,可以直接返回Response或其任何子类,当直接返回Response时,FastAPI会直接传递它,FastAPI 不会用 Pydantic 模型做任何数据转换,不会将响应内容转换成任何类型。这种特性带来了极大的可扩展性,可以返回任何数据类型,重写任何数据声明或者校验等。
在下面的代码中,我们通过JSONResponse 人为返回我们希望返回的内容,代码如下:
from fastapi import FastAPI from pydantic import BaseModel from datetime import datetime from datetime import time from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse app = FastAPI() class Task(BaseModel): name: str created: datetime runat: time creator: str @app.get(path='/test') async def test(): task = Task(name='hello', created=datetime.now(), runat=time(hour=2,minute=30,second=0), creator='admin') jTask = jsonable_encoder(task) return JSONResponse(content=jTask)
执行请求:
curl http://127.0.0.1:8000/test {"name":"hello","created":"2022-01-15T10:53:33.009967","runat":"02:30:00","creator":"admin"}
13.4Response
在FastAPI中,Response接受如下参数:
-
content
str 或者 bytes。
-
status_code
int 类型的 HTTP 状态码。
-
headers
由字符串组成的 dict。
-
media_type
给出媒体类型的 str,比如 "text/html"。
可以直接返回Response,示例代码如下:
from fastapi import FastAPI from fastapi import Response app = FastAPI() @app.get(path='/test') async def test(): data = '''<?xml version="1.0"?> <shampoo> <Header> Apply shampoo here. </Header> <Body> You'll have to use soap here. </Body> </shampoo> ''' return Response(content=data, media_type="application/xml")
执行请求:
curl http://127.0.0.1:8000/test <?xml version="1.0"?> <shampoo> <Header> Apply shampoo here. </Header> <Body> You'll have to use soap here. </Body> </shampoo>
13.5Response子类
-
HTMLResponse
接受文本或字节并返回 HTML 响应。
-
PlainTextResponse
接受文本或字节并返回纯文本响应。
-
JSONResponse
接受数据并返回一个 application/json 编码的响应,是 FastAPI 中使用的默认响应。
-
ORJSONResponse
ORJSONResponse 是一个使用orjson的快速的可选 JSON 响应。
-
UJSONResponse
UJSONResponse 是一个使用 ujson的可选 JSON 响应。
-
RedirectResponse
返回 HTTP 重定向。默认情况下使用 307 状态代码(临时重定向)。
-
StreamingResponse
采用异步生成器或普通生成器/迭代器,然后流式传输响应主体。
-
FileResponse
异步传输文件作为响应。
使用这些子类的方法:
-
可以在路径操作装饰器中通过 response_class 参数声明想要返回的 Response
-
路由操作函数中将返回的内容将被放在对应的Response中 , 如果该 Response 有一个 JSON 媒体类型(application/json),比如使用 JSONResponse 或者 UJSONResponse 的时候,返回的数据将使用在路径操作装饰器中声明的任何 Pydantic 的 response_model 自动转换(和过滤)。
13.5.1ORJSONResponse
ORJSONResponse在性能上有所提高,使用ORJSONResponse需要安装orjson,执行:
pip install orjson
代码如下:
from fastapi import FastAPI from pydantic import BaseModel from datetime import datetime from datetime import time from fastapi.responses import ORJSONResponse app = FastAPI() class Task(BaseModel): name: str created: datetime runat: time creator: str @app.get(path='/test', response_class=ORJSONResponse) async def test(): task = Task(name='hello', created=datetime.now(), runat=time(hour=2,minute=30,second=0), creator='admin') return task
13.5.2 HTMLResponse
代码如下:
from fastapi import FastAPI from fastapi.responses import HTMLResponse app = FastAPI() @app.get(path='/test') async def test(): data = ''' <html> <head> <title>Some HTML in here</title> </head> <body> <h1>Look ma! HTML!</h1> </body> </html> ''' return HTMLResponse(content=data)
上面的代码没有使用response_class 参数,而是直接返回相应的HTMLResponse。
13.5.3PlainTextResponse
代码如下:
from fastapi import FastAPI from fastapi.responses import PlainTextResponse app = FastAPI() @app.get(path='/test', response_class=PlainTextResponse) async def test(): data = 'Hello FastAPI!!!' return data
13.5.4UJSONResponse
在处理某些边缘情况时,ujson 不如 Python 的内置实现那么谨慎。需要安装ujson
pip install ujson
代码如下:
from fastapi import FastAPI from pydantic import BaseModel from datetime import datetime from datetime import time from fastapi.responses import UJSONResponse app = FastAPI() class Task(BaseModel): name: str created: datetime runat: time creator: str @app.get(path='/test', response_class=UJSONResponse) async def test(): task = Task(name='hello', created=datetime.now(), runat=time(hour=2,minute=30,second=0), creator='admin') return task
13.5.5RedirectResponse
代码如下:
from fastapi import FastAPI from fastapi.responses import RedirectResponse app = FastAPI() @app.get(path='/test') async def test(): return RedirectResponse("https://www.baidu.com")
13.5.6StreamingResponse
代码如下:
from fastapi import FastAPI from fastapi.responses import StreamingResponse app = FastAPI() @app.get(path='/test') async def test(): async def read_data(): for i in range(10): data = 'hello fastapi {0} \r\n'.format(i) yield data return StreamingResponse(content=read_data())
执行请求:
curl http://127.0.0.1:8000/test hello fastapi 0 hello fastapi 1 hello fastapi 2 hello fastapi 3 hello fastapi 4 hello fastapi 5 hello fastapi 6 hello fastapi 7 hello fastapi 8 hello fastapi 9
使用StreamingResponse实现流式下载文件,代码如下:
from fastapi import FastAPI from fastapi.responses import StreamingResponse app = FastAPI() @app.get(path='/test') async def test(): def read_file(): with open('Apache_OpenOffice_4.1.11_Win_x86_install_en-US.exe', 'rb') as f: yield from f return StreamingResponse(content=read_file())
当使用浏览器访问http://127.0.0.1:8000/test时开始下载文件。
13.5.7FileResponse
该响应接受不同的参数进行实例化:
-
path
要流式传输的文件的文件路径。
-
headers
任何自定义响应头,传入字典类型。
-
media_type
给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。
-
filename
如果给出,它将包含在响应的 Content-Disposition 中。
代码如下:
from fastapi import FastAPI from fastapi.responses import FileResponse app = FastAPI() @app.get(path='/test') async def test(): return FileResponse(path='Apache_OpenOffice_4.1.11_Win_x86_install_en-US.exe')
当使用浏览器访问http://127.0.0.1:8000/test时开始下载文件。