Sanic 蓝图#
蓝图是可以在应用程序内用于子路由的对象。与其将路由添加到应用程序实例,不如蓝图定义了类似的方法来添加路由,然后以灵活和可插拔的方式与应用程序注册。
蓝图在较大的应用程序中特别有用,因为你的应用程序逻辑可以被分解成几个组或责任区域。
创建和注册蓝图#
首先,你必须创建蓝图。它有一与 Sanic()
应用程序实例非常相似的 API,并且有许多相同的装饰器。
# ./my_blueprint.py
from sanic.response import json
from sanic import Blueprint
bp = Blueprint("my_blueprint")
@bp.route("/")
async def bp_root(request):
return json({"my": "blueprint"})
将此蓝图注册到 app:
from sanic import Sanic
from my_blueprint import bp
app = Sanic(__name__)
app.blueprint(bp)
蓝图还有相同的 websocket()
装饰器和 add_websocket_route
方法来实现 websockets
。
从 v21.12 开始,蓝图可以在向其添加对象之前或之后注册。以前,只有在注册时附加到蓝图的对象才会加载到应用程序实例中。
app.blueprint(bp)
@bp.route("/")
async def bp_root(request):
...
蓝图副本#
蓝图及其附加的所有内容可以使用 copy()
方法复制到新实例。唯一的必需参数是给它传递一个新名称。然而,你也可以使用这个来覆盖旧蓝图中的任何值。
v1 = Blueprint("Version1", version=1)
@v1.route("/something")
def something(request):
pass
v2 = v1.copy("Version2", version=2)
app.blueprint(v1)
app.blueprint(v2)
可得到的路由有:
/v1/something
/v2/something
蓝图组#
蓝图也可以作为列表或元组的一部分进行注册,其中注册者将递归地循环遍历蓝图的任何子序列并相应地进行注册。Blueprint.group()
方法提供了简化此过程的功能,允许“模拟”后端目录结构,模仿前端所看到的内容。考虑这个(相当牵强的)例子:
api/
├──content/
│ ├──authors.py
│ ├──static.py
│ └──__init__.py
├──info.py
└──__init__.py
app.py
# api/content/authors.py
from sanic import Blueprint
authors = Blueprint("content_authors", url_prefix="/authors")
# api/content/static.py
from sanic import Blueprint
static = Blueprint("content_static", url_prefix="/static")
# api/content/__init__.py
from sanic import Blueprint
from .static import static
from .authors import authors
content = Blueprint.group(static, authors, url_prefix="/content")
# api/info.py
from sanic import Blueprint
info = Blueprint("info", url_prefix="/info")
# api/__init__.py
from sanic import Blueprint
from .content import content
from .info import info
api = Blueprint.group(content, info, url_prefix="/api")
# app.py
from sanic import Sanic
from .api import api
app = Sanic(__name__)
app.blueprint(api)
蓝图组前缀和可组合性#
如上面的代码所示,当你创建一组蓝图时,你可以通过将 url_prefix
参数传递给 Blueprint.group
方法来扩展组中所有蓝图的 URL 前缀。这对于为你的 API 创建模拟目录结构很有用。
此外,还有 name_prefix
参数可以用来使蓝图可重用和可组合。当将单个蓝图应用于多个组时,这是特别必要的。通过这样做,蓝图将以每个组的唯一名称进行注册,这允许蓝图多次注册,并且其路由都能正确地以唯一标识符命名。
比如,如下代码创建路由:
TestApp.group-a_bp1.route1
TestApp.group-a_bp2.route2
TestApp.group-b_bp1.route1
TestApp.group-b_bp2.route2
bp1 = Blueprint("bp1", url_prefix="/bp1")
bp2 = Blueprint("bp2", url_prefix="/bp2")
bp1.add_route(lambda _: ..., "/", name="route1")
bp2.add_route(lambda _: ..., "/", name="route2")
group_a = Blueprint.group(
bp1, bp2, url_prefix="/group-a", name_prefix="group-a"
)
group_b = Blueprint.group(
bp1, bp2, url_prefix="/group-b", name_prefix="group-b"
)
app = Sanic("TestApp")
app.blueprint(group_a)
app.blueprint(group_b)
蓝图中间件#
蓝图还可以有专门注册给其端点的中间件。、
@bp.middleware
async def print_on_request(request):
print("I am a spy")
@bp.middleware("request")
async def halt_request(request):
return text("I halted the request")
@bp.middleware("response")
async def halt_response(request, response):
return text("I halted the response")
同样地,使用蓝图组,可以将中间件应用于一组嵌套蓝图的整个组。
bp1 = Blueprint("bp1", url_prefix="/bp1")
bp2 = Blueprint("bp2", url_prefix="/bp2")
@bp1.middleware("request")
async def bp1_only_middleware(request):
print("applied on Blueprint : bp1 Only")
@bp1.route("/")
async def bp1_route(request):
return text("bp1")
@bp2.route("/<param>")
async def bp2_route(request, param):
return text(param)
group = Blueprint.group(bp1, bp2)
@group.middleware("request")
async def group_middleware(request):
print("common middleware applied for both bp1 and bp2")
# Register Blueprint group under the app
app.blueprint(group)
蓝图异常处理#
@bp.exception(NotFound)
def ignore_404s(request, exception):
return text("Yep, I totally found the page: {}".format(request.url))
蓝图可组合#
一个蓝图可以注册到多个组中,每个 BlueprintGroup 本身也可以被注册并进一步嵌套。这创造了无限可能的蓝图组合。
看看这个例子,看看两个处理程序实际上是如何作为五个(5)不同的路由挂载的。
app = Sanic(__name__)
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
group = Blueprint.group(
blueprint_1,
blueprint_2,
version=1,
version_prefix="/api/v",
url_prefix="/grouped",
strict_slashes=True,
)
primary = Blueprint.group(group, url_prefix="/primary")
@blueprint_1.route("/")
def blueprint_1_default_route(request):
return text("BP1_OK")
@blueprint_2.route("/")
def blueprint_2_default_route(request):
return text("BP2_OK")
app.blueprint(group)
app.blueprint(primary)
app.blueprint(blueprint_1)
# The mounted paths:
# /api/v1/grouped/bp1/
# /api/v1/grouped/bp2/
# /api/v1/primary/grouped/bp1
# /api/v1/primary/grouped/bp2
# /bp1