Python 现代开发学习笔记

(十八)用 ASGI 事件表达异步请求

1. scope、receive 与 send

第八章中,协程在 await 时把等待交给事件循环。服务器也可以通过异步接口驱动应用,把请求过程拆成事件;这就是这里要观察的 ASGI 调用。

# 项目文件:demos/asgi_echo.py
# ASGI将请求体按事件交来;应用await接收,再分开发送响应头与响应体。
import json


async def application(scope, receive, send):
    chunks = []
    while True:
        event = await receive()
        if event["type"] == "http.disconnect":
            return
        chunks.append(event.get("body", b""))
        if not event.get("more_body", False):
            break
    value = {
        "method": scope["method"],
        "path": scope["path"],
        "body_bytes": len(b"".join(chunks)),
    }
    body = json.dumps(value).encode("utf-8")
    await send(
        {
            "type": "http.response.start",
            "status": 200,
            "headers": [
                (b"content-type", b"application/json"),
                (b"content-length", str(len(body)).encode("ascii")),
            ],
        }
    )
    await send({"type": "http.response.body", "body": body})

scope 保存方法、路径等连接信息,receive 异步交来请求事件,send 异步发送响应事件。请求体可能分多次到达;more_body 为 True 时继续读取,最后再合并本次正文。收到 disconnect 时停止这次处理。

响应先发 http.response.start,提供状态与头,再发 http.response.body。应用仍然生成相同的 HTTP 响应,变化的是服务器调用 Python 应用的方式。ASGI 的 HTTP 消息

scope保存请求信息,receive依次取得带more_body的片段,应用合并正文后按start和body两个事件发送响应

2. 让 ASGI 服务器运行这个入口

# 在web_project中运行,从demos目录导入asgi_echo模块的application。
uv run uvicorn --app-dir ../demos asgi_echo:application --host 127.0.0.1 --port 8771 --lifespan off

模块名:对象名 指定应用,app-dir 指定导入目录。这里的应用专门处理 HTTP 消息,lifespan off 让服务器不向它发送启动、关闭生命周期消息;下一章再把数据库等资源接到生命周期中。

打开 /hello 可以看到方法、路径和请求体字节数。配套还实际发送中文正文“中文”,按 UTF-8 得到 body_bytes 为 6,与 WSGI 示例的字节处理相互对应。

笔记正文与全部题解 · Ctrl / ⌘ K