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时开始下载文件。










