读取请求数据
文章服务需要根据搜索词查列表,也需要接收反馈。数据可能写在 URL、请求头或请求体里;读取位置不同,处理方式也不同。本专题沿着“查询文章 → 识别客户端 → 提交反馈”的过程使用 Request。
处理函数的 request 参数代表当前请求。query_params、path_params、headers 和 cookies 可以直接读取;读取 JSON 或表单请求体则需要 await。
示例代码见下文:request_data_demo.py 对应最终的 03_article_request_data.py,会依次检查各类请求数据。
读取查询参数与路径参数
查询参数适合搜索、筛选与分页;路径参数用于标识具体文章。路由中的 {article_id:int} 会把路径片段转换为整数,处理函数再从 request.path_params 取值。
项目结构仍然保持简单:
p01c02/
└── 01_query_and_path.py示例文件:01_query_and_path.py。
运行:uvicorn 01_query_and_path:app --reload。
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def search(request):
keyword = request.query_params.get("keyword", "")
page = request.query_params.get("page", "1")
return JSONResponse({
"message": "文章搜索成功",
"keyword": keyword,
"page": page,
})
async def article_detail(request):
article_id = request.path_params["article_id"]
return JSONResponse({
"message": "文章详情读取成功",
"article_id": article_id,
})
routes = [
Route("/search", search),
Route("/articles/{article_id:int}", article_detail),
]
app = Starlette(routes=routes)
if __name__ == '__main__':
import uvicorn
uvicorn.run("01_query_and_path:app", host='127.0.0.1', port=8000, reload=True)这个例子做了两件事:
/search 读取查询参数
/articles/{article_id} 读取路径参数关键代码:
request.query_params.get("keyword", "")
request.query_params.get("page", "1")表示从 URL 查询参数中读取 keyword 和 page。
request.path_params["article_id"]表示从路径参数中读取文章 ID。
启动查询示例
执行:
uvicorn 01_query_and_path:app --reload区分查询参数与路径参数
区分方法很简单:
写在 ? 后面的,是查询参数
写在路径里的,是路径参数例如:
/search?keyword=python&page=1这里 keyword 和 page 是查询参数。
/articles/1001这里 1001 是路径参数。
读取请求头、Cookie 与 JSON
请求头通常描述客户端和内容格式,Cookie 保存客户端随请求带来的小段数据。JSON 放在请求体中;调用 await request.json() 后才能取得解析结果。需要原始字节时可用 await request.body(),本例只处理 JSON。
示例文件:02_headers_cookies_json.py。
:uvicorn 02_headers_cookies_json:app --reload。
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def client_info(request):
user_agent = request.headers.get("user-agent", "")
accept = request.headers.get("accept", "")
visit_token = request.cookies.get("visit_token", "")
return JSONResponse({
"user_agent": user_agent,
"accept": accept,
"visit_token": visit_token,
})
async def feedback(request):
request_data = await request.json()
username = request_data.get("username")
content = request_data.get("content")
return JSONResponse({
"message": "反馈提交成功",
"username": username,
"content": content,
})
routes = [
Route("/client-info", client_info),
Route("/feedback", feedback, methods=["POST"]),
]
app = Starlette(routes=routes)
if __name__ == '__main__':
import uvicorn
uvicorn.run("02_headers_cookies_json:app", host='127.0.0.1', port=8000, reload=True)读取请求头
关键代码:
request.headers.get("user-agent", "")它会读取客户端请求头中的浏览器标识。
读取 Cookie
关键代码:
request.cookies.get("visit_token", "")它会读取名为 visit_token 的 Cookie。
读取 JSON 请求体
关键代码:
data = await request.json()这里要特别注意:读取 JSON 请求体需要 await。
验证 GET 接口
访问:
http://127.0.0.1:8000/client-info使用浏览器查看页面时,请求通常会自动带上 user-agent;演示脚本则显式设置该请求头。
验证 JSON 提交
启动 02_headers_cookies_json.py 后,可用 requests.post(..., json=...) 向下面地址发送 POST 请求:
http://127.0.0.1:8000/feedback脚本会打印状态码和解析后的响应体。
服务端会把 JSON 中的字段读出来,再返回给客户端。
读取表单数据
表单和 JSON 都通过请求体提交,但编码方式不同。读取表单使用 await request.form();运行本例前还需安装课程依赖中的 python-multipart。
很多浏览器表单提交不是 JSON,而是表单数据。
这时可以使用:
await request.form()示例文件:03_article_request_data.py。
运行:uvicorn 03_article_request_data:app --reload。
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def form_login(request):
form = await request.form()
username = form.get("username", "")
password = form.get("password", "")
return JSONResponse(
{
"message": "表单提交成功",
"username": username,
"password": password,
}
)
routes = [
Route("/form-login", form_login, methods=["POST"]),
]
app = Starlette(routes=routes)这里的流程是:
客户端提交表单
↓
request.form() 读取表单数据
↓
服务端取出 username 和 password
↓
返回 JSON 结果验证表单接口
可运行本专题的 request_data_demo.py,向下面地址发送 POST 请求:
http://127.0.0.1:8000/form-login脚本会打印状态码和解析后的响应体。
JSON 与表单的区别
这里可区分为:
JSON 更适合结构化接口数据
表单更常见于传统网页提交两者都属于请求体,但读取方式不同:
await request.json()
await request.form()文章查询与反馈案例
最后把搜索、详情和反馈放到同一个应用中。
目标包括:
/search 查询参数
/articles/{article_id:int} 路径参数
/client-info 请求头和 Cookie
/feedback JSON 请求体
/form-login 表单数据示例文件:03_article_request_data.py。
运行:uvicorn 03_article_request_data:app --reload。
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def search_articles(request):
keyword = request.query_params.get("keyword")
page = request.query_params.get("page", "1")
return JSONResponse({
"message": "文章搜索成功",
"keyword": keyword,
"page": page,
})
async def article_detail(request):
article_id = request.path_params["article_id"]
return JSONResponse({
"message": "文章详情读取成功",
"article_id": article_id,
})
async def client_info(request):
user_agent = request.headers.get("user-agent")
accept = request.headers.get("accept")
visit_token = request.cookies.get("visit_token")
return JSONResponse({
"user_agent": user_agent,
"accept": accept,
"visit_token": visit_token,
})
async def feedback(request):
request_data = await request.json()
username = request_data.get("username")
content = request_data.get("content")
return JSONResponse({
"message": "反馈提交成功",
"username": username,
"content": content,
})
async def form_login(request):
form_data = await request.form()
username = form_data.get("username", "")
password = form_data.get("password", "")
return JSONResponse({
"message": "表单提交成功",
"username": username,
"password": password,
})
routes = [
Route("/search", search_articles),
Route("/articles/{article_id:int}", article_detail),
Route("/client-info", client_info),
Route("/feedback", feedback, methods=["POST"]),
Route("/form-login", form_login, methods=["POST"]),
]
app = Starlette(routes=routes)
if __name__ == '__main__':
import uvicorn
uvicorn.run("03_article_request_data:app", host='127.0.0.1', port=8000, reload=True)案例需求
这个案例把几种请求数据的读取方式放进同一个应用:
从 URL 读
从路径读
从请求头读
从 Cookie 读
从 JSON 读
从表单读运行案例
执行:
uvicorn 03_article_request_data:app --reload1、访问 /search
访问:
http://127.0.0.1:8000/search?keyword=python&page=12、访问 /articles/{article_id}
例如:
http://127.0.0.1:8000/articles/10013、访问 /client-info
可以直接访问:
http://127.0.0.1:8000/client-info如果需要测试 Cookie,可以用工具手动带上 Cookie 再观察返回结果。
4、访问 /feedback
向下面地址发送 POST JSON:
http://127.0.0.1:8000/feedback5、访问 /form-login
向下面地址发送表单 POST:
http://127.0.0.1:8000/form-login案例源码
import requests
BASE_URL = "http://127.0.0.1:8000"
def test_search():
response = requests.get(
f"{BASE_URL}/search",
params={"keyword": "starlette", "page": 2},
timeout=5,
)
print("GET /search", response.status_code, response.json())
def test_article_detail():
response = requests.get(f"{BASE_URL}/articles/1", timeout=5)
print("GET /articles/1", response.status_code, response.json())
def test_client_info():
response = requests.get(
f"{BASE_URL}/client-info",
headers={"User-Agent": "Starlette-Course"},
cookies={"visit_token": "abc123"},
timeout=5,
)
print("GET /client-info", response.status_code, response.json())
def test_feedback():
response = requests.post(
f"{BASE_URL}/feedback",
json={"username": "zhengxin", "content": "学习 Request"},
timeout=5,
)
print("POST /feedback", response.status_code, response.json())
def test_form_login():
response = requests.post(
f"{BASE_URL}/form-login",
data={"username": "zhengxin", "password": "123456"},
timeout=5,
)
print("POST /form-login", response.status_code, response.json())
if __name__ == "__main__":
# 启动 03_article_request_data.py 后依次观察五种读取方式。
test_search()
test_article_detail()
test_client_info()
test_feedback()
test_form_login()