# Version 21.3

# 介绍(Introduction)

Sanic 现在变得更快了。

嗯,已经很快了。但是随着 v21 版本的第一次迭代更新,我们整合了一些重大的里程碑式的变化,并做出了一些切实的改进。这包含了一些已经酝酿多年的想法,并最终成为发行版。

突破性变化

21.3 版本引入了很多新特性。同时也带来了一些突破性变化。这就是为什么这些变化要在上一个长期支持版(LTS)发布后引入的原因。如果您依赖已经被删除的内容,那么您应该继续使用 v20.12LTS 直到您可以对其进行升级。

pip install "sanic>=20.12,<20.13"
pip freeze > requirements.txt

对于大多数的安装,您可以非常轻松的升级。

# 更新内容(What to know)

值得注意的新功能或突破性变化,以及更新内容...

# 仅支持 Python3.7 及以上版本(Python 3.7+ Only)

新版本放弃了对 Python3.6 的支持,20.12LTS 将是最后一个支持 Python3.6 的长期支持版,直到2022 年 12 月停止维护。19.12LTS 将继续支持 Python3.6,直到 2021 年 12 月停止维护。

阅读我们的 发行安排 以了解更多内容。

# 流式传输合并为单个流(Streaming as first class citizen)

将请求/响应周期统一为单个流极大的提升了速度。以前,常规周期和流式传输周期之间存在差异。这已经简化在底层代码中, 即 Api 现在保持不变, 以兼容性的方式进行更新。令人高兴的是,现在 所有 的流式请求都合并到了单个流中。

阅读 流式传输更新的内容 以了解更多

# 全新的路由器(Router overhaul)

旧的 Sanic 路由器是基于正则表达式的。此外,它还存在一些缺陷,使得在运行时很难修改,并导致一些性能问题。此次变化已经酝酿多年,现在在启动时将路由器转换为编译树 (opens new window)。我们也期待今年会有更多性能上的改进。

对外的 API 保持了向后的兼容性。然而,如果您之前调用过路由器内部的方法,您应该注意以下一些变化。比如:

  1. Router.get() 有了一个全新的返回值
  2. Router 现在不是 namedtuple,而是一个普通的对象
  3. 如果手动构建了路由器,您需要在最后调用 Router.finalize() 方法
  4. 路由器有了一个新的匹配模式 date:ymd
  5. 您不能在没有定义任何路由的情况下启动应用

新的路由器拥有独立的仓库: sanic-org/sanic-router (opens new window) 和自己的 PyPI 项目 (opens new window)

# 信号 API(Signals API) ⭐️

当前为 测试功能,该 API 将于 v21.6 版本正式发布

新的 Sanic 路由器的一个好处是,它可以同时为 新的信号 API (opens new window) 提供支持。这个特性现在被发布给公众使用,正式发布时,该接口可能保持现有用法。

该功能的核心思想是:

  1. 为了允许开发者更好地控制和访问请求的生命周期
  2. 提供一个工具来使您的响应函数协调和发送信息
  3. 进一步提高性能

该功能提供了三种新的使用方法:

用法 说明
@app.signal(...) 用于定义一个信号处理程序,它的用法和 route 十分相似,无论在何处收到信号,该程序都将被执行。
app.event(...) 一个可在应用程序中的任何地方使用的变量,用于暂停执行,直到事件被触发。
app.dispatch(...) 触发事件,并运行信号处理程序
@app.signal("foo.bar.<thing>")
async def signal_handler(thing, **kwargs):
    print(f"[signal_handler] {thing=}", kwargs)
async def wait_for_event(app):
    while True:
        print("> waiting")
        await app.event("foo.bar.*")
        print("> event found\n")
@app.after_server_start
async def after_server_start(app, loop):
    app.add_task(wait_for_event(app))
@app.get("/")
async def trigger(request):
    await app.dispatch("foo.bar.baz")
    return response.text("Done.")

# 路由名称(Route naming)

曾经,路由可以通过 route.nameroute.endpoint 进行引用。虽然看起来几乎一样,但是还是存在有细微的差别。现在,所有的路由都将统一命名和使用。

<app name>.[optional:<blueprint name>.]<handler name>

这个新的 “name” 将会被分配该属性 route.name。我们取消了 route.enpoint 属性,并将在 v21.9 版本中正式停用。在此之前,它的别名仍将是 route.name

除此之外,以前用于静态文件,websocket 和蓝图的命名前缀也将被移除。

# 全新的装饰器(New decorators)

新增数种装饰器,取代原有的装饰器,方便 IDE 进行自动补全

# Alias to @app.listener("...")
@app.before_server_start
@app.after_server_stop
@app.before_server_start
@app.after_server_stop
# Alias to @app.middleware("...")
@app.on_request
@app.on_response

# 路由解码(Unquote in route)

如果您的路由参数使用了非 ascii 码的字符,Sanic 将不再为您进行解码,您需要显式传参来告诉路由它应该为您解码。

@app.route("/overload/<param>", methods=["GET"], unquote=True)
async def handler2(request, param):
    return text("OK2 " + param)
request, response = app.test_client.get("/overload/你好")
assert response.text == "OK2 你好"

如果您忘了这样做,您的响应文本将是保持编码状态的文本。

# 可修改的 Request.match_info(Alter Request.match_info)

match_info 始终为匹配的路径参数提供数据。您现在可以修改它,例如在中间件中。

@app.on_request
def convert_to_snake_case(request):
    request.match_info = to_snake(request.match_info)

# 路由版本(Version types in routes)

现在,路由中的 version 参数可以使用:

  • str
  • int
  • float
@app.route("/foo", version="2.1.1")
@app.route("/foo", version=2)
@app.route("/foo", version=2.1)

# 安全的 Body 处理方式(Safe method handling with body)

默认情况下, GETHEADOPTIONSDELETE 方法将不再对请求体进行解码,您可以通过以下的方式取消这一特性:

@app.delete(..., ignore_body=False)

# 应用,蓝图和蓝图组的奇偶校验(Application, Blueprint and Blueprint Group parity)

SanicBlueprint 类 具有很多类似的方法,在以前,由于他们的实现策略略有不同,他们使用了很多具有重复功能的代码。既然他们都继承自同一个基类,开发者和插件应该有一个统一的 API 来进行工作。

此外,蓝图组也支持常见的 URL 拓展参数了,例如:versionstrict_slashes

# 放弃对 httpx 的依赖(Dropped httpx from dependencies)

现在 Sanic 不再依赖 httpx

# 移除测试库(Removed testing library)

Sanic 原本自带的测试客户端已经被移除。该测试客户端现在拥有了独立的仓库:sanic-org/sanic-testing (opens new window) 自己的 PyPI 项目 (opens new window)

如果您已经安装了 sanic-testing ,那么当您创建了 Sanic() 应用时测试客户端也同时可用。所以 唯一 的变化就是您需要添加 sanic-testing 到您测试工具的依赖中。

# 应用和连接级别的上下文对象(Application and connection level context (ctx) objects)

19.9 版本 添加 (opens new window)request.ctx 的 API。这个对象可以轻松地将属性和数据附加到请求对象上(比如在中间件中),并在应用程序的其他地方重复使用这些信息。

同样的,这个概念也被拓展到了下面两处:

  1. 应用实例
  2. 传输连接

# 应用上下文(Application context)

一个常见的使用场景是将属性附加到应用程序实例上。为了保持一致性,并避免与 Sanic 自身属性的名称冲突,ctx 对象现在存在于 Sanic 实例上。

@app.before_server_startup
async def startup_db(app, _):
    # WRONG
    app.db = await connect_to_db()
    # CORRECT
    app.ctx.db = await connect_to_db()

# 连接上下文(Connection context)

当一个客户端发送了一个包含 keep-alive 头的请求,Sanic 将尝试保持这个传输套接字 一段时间。现在这个传输对象也有一个可用的 ctx 对象了。这实际上意味着来自同一个客户端的多个请求(就在这里传输层被复用)可以共享状态。

@app.on_request
async def increment_foo(request):
    if not hasattr(request.conn_info.ctx, "foo"):
        request.conn_info.ctx.foo = 0
    request.conn_info.ctx.foo += 1
@app.get("/")
async def count_foo(request):
    return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}")
$ curl localhost:8000 localhost:8000 localhost:8000
request.conn_info.ctx.foo=1
request.conn_info.ctx.foo=2
request.conn_info.ctx.foo=3

注意

连接级别的上下文是一个实验性的功能,并且应该会在 v21.6 版本中被完善。

# 新闻(News)

# 一个新的前端(A NEW frontpage) 🎉

我们把文档分成了两部分,储存库中的文档将仍旧使用 ReadTheDocs 进行构建,但仅限于 API 文档。新的前端页面将使用 “Sanic 用户指南”。

新的站点基于 vuepress 构建,欢迎投稿,我们也会请人对文档进行翻译。

作为文档的一部分,我们同样更新了 ReadTheDocs 上的文档,但仅限于 API 接口文档。

# 聊天室移至 Discord(Chat has moved to Discord)

Gitter 聊天室向着淘汰又迈进了一步。取而代之的将是我们新开放的 Discord 聊天室 (opens new window)

# 资助 Sanic(Open Collective)

Sanic 社区组织在 Open Collective 上申请了捐献项目,任何愿意为 Sanic 发展提供资金支持的人可以 点此 (opens new window) 参与。

# 2021 发布经理(2021 Release Managers)

感谢 @sjsadowski 和 @yunstanford 担任 2019 年和 2020 年的发布经理。今年的发布经理是 @ahopkins 和 @vltr。

# 鸣谢(Thank you)

感谢所有参与此次发布的人:👏

@ahopkins (opens new window) @akshgpt7 (opens new window) @artcg (opens new window) @ashleysommer (opens new window) @elis-k (opens new window) @harshanarayana (opens new window) @sjsadowski (opens new window) @tronic (opens new window) @vltr (opens new window)

感谢 @ConnorZhang (opens new window)@ZinkLu (opens new window) 将文档翻译成中文。


请务必查看更新日志以获取指向所有 PR 等的链接。

MIT Licensed
Copyright © 2018-present Sanic Community Organization

~ Made with ❤️ and ☕️ ~