示例代码

服务器向客户端写入Cookie数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@app.get("/async_set_cookie")
async def set_cookie_to_client(response: Response):
    """
    将 cookie 写入到本地文件: test_cookies
    curl -c test_cookies -X 'GET' \
      'http://127.0.0.1:18081/async_set_cookie' \
      -H 'accept: application/json'

    cat test_cookies
    # Netscape HTTP Cookie File
    # https://curl.se/docs/http-cookies.html
    # This file was generated by libcurl! Edit at your own risk.

    127.0.0.1       FALSE   /       FALSE   0       test_cookie_user_id     test_cookie_user_value
    """
    response.set_cookie(key = 'test_cookie_user_id', value = 'test_cookie_user_value')
    return {"set_cookie": datetime.datetime.now().isoformat(sep = ' ')}

向客户端设置一个名为 test_cookie_user_id 的 cookie,并返回当前的时间戳。

使用 curl 命令作为客户端,向服务器发送请求并接收服务端设置的 cookie 数据:

1
2
3
curl -c test_cookies -X 'GET' \
  'http://127.0.0.1:18081/async_set_cookie' \
  -H 'accept: application/json'

查看 cookie 数据:cat test_cookies

1
2
3
4
5
6
cat test_cookies               
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

127.0.0.1       FALSE   /       FALSE   0       test_cookie_user_id     test_cookie_user_value

服务端接收客户端传入的Cookie数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@app.get('/async_with_cookie')
async def request_with_cookie(req: Request):
    """
    从本地缓存文件中读取 cookie,并发送给服务器
    curl -b test_cookies -X 'GET' \
    'http://127.0.0.1:18081/async_with_cookie' \
    -H 'accept: application/json'

    it's generally recommended and more common to use cookies for storing simple string values rather than complex data structures
    Cookie Parameters:
        These are used to extract data from HTTP cookies sent by the client.
        Cookies are small pieces of data stored on the client-side and sent with every request to the server.
        They're often used for maintaining session information, user preferences, or tracking.
    Path Parameters:
        These are part of the URL path itself.
        They're typically used for identifying specific resources or entities.
        Example: /users/{user_id} where user_id is a path parameter.
    Query Parameters:
        These are appended to the URL after a question mark.
        They're used for filtering, sorting, or passing optional data.
        Example: /users?age=30&city=NewYork
    Body Parameters:
        These are sent in the request body, typically with POST, PUT, or PATCH methods.
        They're used for sending larger amounts of data or complex structures.
        Often used for form submissions or API payloads.
        The key differences and use cases:
    Data Source:
        Cookies come from the browser's cookie storage.
        Path and query parameters come from the URL.
        Body parameters come from the request body.
    Persistence:
        Cookies can persist across multiple requests and even browser sessions.
        Path, query, and body parameters are typically single-request.
    Visibility:
        Path and query parameters are visible in the URL.
        Cookies and body parameters are not visible in the URL.
    Size Limitations:
        Cookies and URL parameters (path and query) have size limitations.
        Body parameters can handle much larger data sets.
    Use Cases:
        Cookies: Session management, remembering user preferences.
        Path Parameters: Identifying resources (e.g., user IDs, post IDs).
        Query Parameters: Filtering, sorting, pagination.
        Body Parameters: Submitting forms, complex data structures.
    """
    print('received cookies:', req.cookies)
    return {"cookies": req.cookies}

使用 curl 客户端从本地缓存文件中读取 cookie,并发送给服务器:

1
2
3
curl -b test_cookies -X 'GET' \
'http://127.0.0.1:18081/async_with_cookie' \
-H 'accept: application/json'

服务端的打印日志:

1
2
received cookies: {'test_cookie_user_id': 'test_cookie_user_value'}
INFO:     127.0.0.1:53670 - "GET /async_with_cookie HTTP/1.1" 200 OK

img

服务端日志

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Cookies(BaseModel):
    session_id: str
    fatebook_tracker: str | None = None
    googall_tracker: str | None = None

    class Config:
        title = 'test cookie model'
        extra = "forbid"


@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
    """
    curl -X 'GET' \
    'http://127.0.0.1:18081/items/' \
    -H 'accept: application/json' \
    -H 'Cookie: googall_tracker=123; session_id=test; fatebook_tracker=HelloWorld;'
    """
    return cookies

注意:生成的 Swagger 文档进行测试时无法正常执行,这应该是文档的测试 case 生成有问题:

Issue

img

填入测试参数

img

错误的执行参数

使用 Curl 正确执行:

img

多个键值对

Request 参数与显式的 Path、Query、Body、Cookie、Header 参数的使用场景划分

FastAPI’s Path, Query, Body, Cookie, and Header parameters are generally preferred for their type safety, validation, and auto-documentation features, there are specific scenarios where using the Request object directly can be beneficial.

推荐使用显式的 Path、Query、Body、Cookie、Header 参数的原因

  • Type safety: FastAPI performs type checking and conversion automatically.
  • Validation: Pydantic models used for body parameters provide built-in validation.
  • Auto-documentation: FastAPI generates OpenAPI (Swagger) documentation automatically based on your function signatures.
  • Performance: FastAPI optimizes the parameter extraction process.
  • Clarity: The function signature clearly shows what parameters are expected.

适合使用 Request 的场景

Summary

  • When you need to access raw request data.
  • For debugging or logging purposes.
  • When building middleware or plugins that need to inspect the raw request.
  • When dealing with non-standard or dynamic parameter structures.

Dynamic or Unknown Parameter Structure

When you’re dealing with requests where the structure or names of parameters are not known in advance or can vary significantly.

1
2
3
@app.post("/dynamic")
async def handle_dynamic(request: Request):
    data = await request.json()

Raw Data Access

When you need access to the raw request data, such as for custom parsing or when dealing with non-standard content types.

1
2
3
@app.post("/raw")
async def handle_raw(request: Request):
    raw_body = await request.body()

Custom Middleware or Plugins

When building middleware or plugins that need to inspect or modify the raw request.

1
2
3
4
5
@app.middleware("http")
async def custom_middleware(request: Request, call_next):
    # Inspect or modify request
    response = await call_next(request)
    return response

Debugging or Logging

When you need to log or debug the entire request, including headers, body, and other metadata.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@app.get("/debug")
async def debug_request(request: Request):
    log_data = {
        "method": request.method,
        "url": str(request.url),
        "headers": dict(request.headers),
        "query_params": dict(request.query_params),
        "client": request.client.host
    }
    logger.debug(f"Request data: {log_data}")

Working with WebSockets

When handling WebSocket connections, you often need the Request object for the initial connection setup.

1
2
3
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()

Accessing Multiple Aspects of the Request

When you need to access various parts of the request that don’t fit neatly into the standard parameter types.

1
2
3
4
5
6
@app.post("/complex")
async def handle_complex(request: Request):
    body = await request.json()
    headers = request.headers
    query_params = request.query_params
    client_host = request.client.host

File Uploads with Additional Metadata

When handling file uploads where you need access to both the file and additional form data.

1
2
3
4
5
@app.post("/upload")
async def upload_file(request: Request):
    form = await request.form()
    file = form["file"]
    metadata = form["metadata"]

Custom Content Types:

When dealing with custom or non-standard content types that FastAPI doesn’t handle out of the box.

1
2
3
4
@app.post("/custom-type")
async def handle_custom_type(request: Request):
    if request.headers.get("content-type") == "application/custom-type":
        data = await request.body()