ASGI翻译系列(二):使用 ASGI 和 HTTP
我们之前的文章介绍了 ASGI 协议,并介绍了为什么拥有一个标准化的低级server/application接口是有用的,以及 Python 社区超越现有 WSGI 服务器并开始采用 ASGI 的一些动机。
在这篇文章中,我们将开始看看 ASGI 的构建快,并演示我们如何开始使用它们来编写 web 服务。
作为一个应用程序开发者,你通常不会在低级别的地方使用ASGI,因为框架通常会提供一个更高级别的接口来工作。
译者注:原文代码的使用的ASGI2协议,当时接口采用的是双重调用,在2019年3月20日,ASGI3.0发布,改进了调用风格,虽然向下兼容,但是原文的例子有些还是没法用了,所以我这边做了更新统一采用ASGI3写法并适当进行增删改。
具体可以看ASGI3.0
1. ASGI应用
ASGI的结构是一对可调用的接口。
第一个API调用是一个常规函数调用,它是为了建立一个新的有状态上下文。
第二个API调用是一个异步调用,它提供了一对通信通道,服务器和客户端通过这个通道互相发送信息。
下面是基本结构的样子:
1 |
|
上面的例子是ASGI2双重调用的风格,新的 ASGI3.0应用程序看起来是这样的:
1 |
|
让我们来看看这些接口的参数:
1.1 Scope
一个信息字典,用来设置应用程序的状态。
ASGI可以用于各种接口,而不仅仅是HTTP,所以这个字典中最重要的键是 “type “键,它用来确定设置的是什么样的消息接口。
下面是一个简单的HTTP GET请求 https://www.example.org/
的scoop的例子:
1 |
|
1.2 Send
一个接受单个消息参数并返回 None 的异步函数。在 HTTP 的情况下,这个消息通道用于发送 HTTP 响应。
有两种类型的 HTTP 响应消息: 一种用于初始化发送响应,另一种用于发送响应正文。
1 |
|
1.3 Receive
一个不带参数的异步函数,该函数返回一条消息。在使用HTTP的情况下,此消息传递通道用于使用HTTP请求正文。
1 |
|
2. 我们的第一个“ Hello,World!”应用程序
让我们把所有这些放在我们的第一个简单的 ASGI 应用程序中:
example.py
1 |
|
你现在可以使用任何 ASGI 服务器运行该应用程序,包括 daphne、 uvicorn 或 hypercorn。
1 |
|
现在在你的网页浏览器中打开“ http://127.0.0.1:8000
也许现在还不是特别令人兴奋?尽管如此,它仍然是一整套功能的基础,而这些功能在 Python 现有的 WSGI 接口中是不可能实现的。
3. 构造 ASGI 应用程序的不同方法(适用于ASGI2)
有多种方法可以构建一个 ASGI 应用程序。
3.1 使用闭包将scope绑定到 ASGI 实例
1 |
|
3.2 使用 functools.partial 将scope绑定到 ASGI 实例
1 |
|
3.3 使用基于类的接口将scope绑定到一个 ASGI 实例
1 |
|
基于类的接口将在 ASGI 实现中非常常见,因为它实例化了一个对象,在单个请求/响应周期的生命周期中可以对其状态进行操作。
4. 更高层次的工作
虽然理解 ASGI 的工作原理很重要,但是你不希望大部分时间都在低层接口上工作。
Starlette 库提供了请求和响应类,可用于处理读取传入的HTTP请求和发送传出的响应的底层细节。
4.1 HTTP Requests
Request 类接受一个 ASGI scope,也可以选择receive通道,并在请求上显示一个更高级别的接口。
1 |
|
request类使下列接口可用:
request.method
- The HTTP method.request.url
- A string-like interface that also gives you access to the parsed components of the URL. egrequest.url.path
.request.query_params
- A multi-dict, containing the parsed URL query parameters.request.headers
- A case-insensitive multi-dict, containing the HTTP headers.request.cookies
- A dictionary of string values, representing all the cookie data included in the request.async request.body()
- An asynchronous method for returning the request body as bytes.async request.form()
- An asynchronous method for returning the request body parsed as HTML form data.async request.json()
- An asynchronous method for returning the request body parsed as JSON data.async request.stream()
- An asynchronous iterator for consuming the request stream chunk-by-chunk without reading everything into memory.
4.2 HTTP Responses
Starlette 包括各种处理发送回传出 HTTP 响应的 Response 类。
下面是一个同时使用请求和响应的示例:
example.py
1 |
|
响应实例与其他任何 ASGI 实例呈现相同的接口。
要真正发送响应,你可以用同样的方式来称呼它:
await response(scope, receive, send)
这是一个很好的属性,因为它意味着我们可以像使用 ASGI 应用程序的后半部分一样使用响应实例。
运行我们的应用程序:
1 |
|
5. 总结
我们已经掌握了第一个 ASGI“ Hello,World”应用程序。
虽然理解 ASGI 消息传递的基本原理很重要,但这不是我们开发的时候要花费时间的地方,因此我们也看到了如何开始将这些细节抽象到更高级别的请求/响应接口中。
我们讨论了以下术语,每当我们谈论使用ASGI的机制时,都需要这些术语:
- ASGI Application - 满足 ASGI 接口的应用程序
- Scope - 用于实例化 ASGI 应用程序的信息字典
- Receive, Send - server/application消息传递发生的一对通道
- Message - 通过接收或发送通道发送的信息字典
我们还开始使用Starlette软件包,该软件包为我们提供了在更高级别的界面上与ASGI一起使用所需的基本工具集。
在本系列的下一篇文章中,我们将更详细地探讨ASGI HTTP消息传递。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!