Request摘要

Request是FastAPI针对客户端请求信息提供的访问接口,因此要更简便地处理请求信息可以使用Request,而要更个性话地处理请求信息可以直接访问ASGI scope和receive channel。

async def app(scope, receive, send):
    assert scope['type'] == 'http'
    request = Request(scope, receive)
    content = '%s %s' % (request.method, request.url.path)
    response = Response(content, media_type='text/plain')
    await response(scope, receive, send)

Request包含的信息和功能

Request通过一系列的方法、属性集合来访问客户端请求信息,主要包括:
1、request.method:HTTP请求方法

@app.get("/req_method")
async def req_method(request: Request):
    # request.method 返回HTTP请求方法
    return {
        "method": request.method
    }

在这里插入图片描述

2、request.client:客户端信息集合

@app.get("/req_client")
async def req_client(request: Request):
    # request.client返回一个Address(host: str port: int)命名元组(namedtuple)
    #
    return {
        "host": request.client.host,
        "port": request.client.port
    }

在这里插入图片描述
request.url:请求URL

@app.get("/req_url")
async def req_url(request: Request):
    # request.url返回一个URL Components对象(scheme:str, netloc:str:str, path:str, query:str, fragment:str, username:str|None, password:str|None, hostname:str|None, port:int, is_secure:bool)
    # scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
    return {
        "scheme": request.url.scheme,
        "netloc": request.url.netloc,
        "path": request.url.path,
        "query": request.url.query,
        "fragment": request.url.fragment,
        "username": request.url.username,
        "password": request.url.password,
        "hostname": request.url.hostname,
        "port": request.url.port,
        "is_secure": request.url.is_secure
    }

在这里插入图片描述
事实上,request还包含了一个url构造方法request.url_for(),用以构造URL对象。
同时,request.url返回的URL Components对象中还包含了对query_params进行操作的几个方法include_query_params,replace_query_params,remove_query_params。但这些操作在当前语境下似乎没有太大意义。
3、request.base_url:请求的基准url,同样返回一个URL Components对象,在此不做赘述。
4、request.headers:请求头信息集合

@app.get("/req_headers")
async def req_headers(request: Request):
    # request.headers返回一个Headers对象,主要包括了http header原始信息(request.headers.raw)和解析后的键值对(request.headers.items())
    return request.headers

在这里插入图片描述
5、request.query_params:URL查询参数集合

@app.get("/req_query_params")
async def req_query_params(request: Request):
    # request.query_params返回一个QueryParams字典,可以通过get方法获取参数值
    # http://127.0.0.1:8088/req_query_params?name=jack&hobby=music
    return request.query_params

在这里插入图片描述
6、request.path_params:URL路径参数集合

@app.get("/req_path_params/{name}/{hobby}")
async def req_path_params(request: Request, name: str, hobby: str):
    # request.path_params返回一个字典
    # http://127.0.0.1:8088/req_path_params/jack/music
    return {
        "path_name": name,
        "path_hobby": hobby,
        "path_params": request.path_params
    }

在这里插入图片描述

7、request.cookies:浏览器cookie集合

@app.get("/req_cookies")
async def req_cookies(request: Request):
    # request.path_params返回一个字典
    return request.cookies

在这里插入图片描述
8、request.form():表单信息集合

@app.post("/req_form")
async def req_form(request: Request, name: str = Form(...), hobby: str = Form(...), ):
    return {
        "form_name": name,
        "form_hobby": hobby,
        "form_params": await request.form()
    }

在这里插入图片描述
以下为从request.form()获得客户端上传文件的信息。

@app.post("/req_file")
async def req_file(name: str = Form(...), resume_file: UploadFile = File(...)):
    res = {
        "name": name,
        "resume_file": resume_file,
        "resume_file_name": resume_file.filename,
        "resume_file_headers": resume_file.headers,
        "resume_file_size": resume_file.size,
        "resume_file_content_type": resume_file.content_type,
        "resume_file_content": await resume_file.read(),
    }
    return res

在这里插入图片描述
10、request.json():把请求体解析为json

@app.post("/req_json")
async def req_json(request: Request,json_data: dict):
    # request.json() 返回json,此时可以利用pydantic进行json数据校验并映射到一个model类实例
    return await request.json()

在这里插入图片描述

11、request.body():原始请求体

@app.post("/req_body")
async def req_body(request: Request):
    # request.body() 返回二进制数据
    return await request.body()

共享Request中的数据

1、在FastAPI中Request请求信息将依次经过middleware、router等组件进行处理,但request所包含的信息修改后在scope中是不可改变的,这意味着上一环节处理后不能直接通过修改相应的值从而来传递值。
因此以下代码是无法成立的:

@app.middleware("http")
async def req_scope_middleware(request: Request, call_next):
    request.headers['new-header'] = 'new-header-value'
    return await call_next(request)

@app.get("/req_scope")
async def req_scope(request: Request):
    return request.headers

那如果需要在middleware中修改Request请求信息以方便后续程序使用,可以通过以下方式来完成:

@app.middleware("http")
async def req_scope_middleware(request: Request, call_next):
    headers = dict(request.scope['headers'])
    headers[b'new-header'] = 'new-header-value'.encode('utf-8')
    request.scope['headers'] = [(k, v) for k, v in headers.items()]
    return await call_next(request)

@app.get("/req_scope")
async def req_scope(request: Request):
    return request.headers

在这里插入图片描述

2、request.state:在一个请求的生命周期中存储和共享数据
对于上面的应用场景中,要在request传递自定义的信息更方便的方式是使用request.state。request.state在每个请求开始时都会被初始化为一个空对象,你可以将任何属性和值添加到这个对象中。这对于将数据从依赖项传递到路由处理程序或者在同一个请求的不同路由处理程序之间共享数据非常有用。

@app.middleware("http")
async def req_state_middleware(request: Request, call_next):
    request.state.name = "tom"
    request.state.hobby = "music"
    return await call_next(request)


@app.get("/req_state")
async def req_state(request: Request):
    return {
        "state_name": request.state.name,
        "state_hobby": request.state.hobby
    }

在这里插入图片描述
3、request.session:用以请求之间共享的会话信息
FastAPI在Request对象中还提供了session对象用以共享请求之间的会话信息,可用于跟踪用户状态、存储用户特定信息(如身份验证令牌或用户偏好)等场景。request.session基于SessionMiddleware,要使用request.session需要在程序中加载SessionMiddleware。session为dict类型,可以按dict的操作方式进行操作。

# 引入SessionMiddleware
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(SessionMiddleware, secret_key="my_secret_key")

@app.get("/req_session")
async def req_session(request: Request):
    request.session['name'] = "tom"
    request.session['hobby'] = "music"
    return request.session

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐