蓝图

蓝图是可以在应用程序中用于子路由的对象。蓝图不是向应用程序实例添加路由,而是定义类似的方法来添加路由,然后以灵活且可插入的方式向应用程序注册路由。

蓝图对于更大的应用程序特别有用,在这些应用程序中,可以将应用程序逻辑分解为多个组或职责区域。

我的第一张蓝图

下面展示了一个非常简单的蓝图,它在根目录中注册了一个处理函数 / 你的申请。

假设您将此文件另存为 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'})

正在注册蓝图

蓝图必须在应用程序中注册。

from sanic import Sanic
from my_blueprint import bp

app = Sanic(__name__)
app.blueprint(bp)

app.run(host='0.0.0.0', port=8000, debug=True)

这将把蓝图添加到应用程序中,并注册由蓝图定义的任何路由。在本例中,在 app.router 看起来像:

[Route(handler=<function bp_root at 0x7f908382f9d8>, methods=frozenset({'GET'}), pattern=re.compile('^/$'), parameters=[], name='my_blueprint.bp_root', uri='/')]

蓝图组和嵌套

蓝图也可以作为列表或元组的一部分进行注册,其中注册者将递归地遍历蓝图的任何子序列并相应地进行注册。这个 Blueprint.group 方法是为了简化此过程而提供的,允许“模拟”后端目录结构模拟从前端看到的内容。考虑这个(相当做作的)例子:

原料药/
├——含量/
多位作者
}├——静态.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 现在可以这样做:

# app.py
from sanic import Sanic

from .api import api

app = Sanic(__name__)

app.blueprint(api)

使用蓝图

蓝图与应用程序实例具有几乎相同的功能。

WebSocket路由

WebSocket处理程序可以使用 @bp.websocket 装饰工 bp.add_websocket_route 方法。

蓝图中间件

使用蓝图还允许您全局注册中间件。

@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))

静态文件

静态文件可以在蓝图前缀下全局提供服务。

# suppose bp.name == 'bp'

bp.static('/web/path', '/folder/to/serve')
# also you can pass name parameter to it for url_for
bp.static('/web/path', '/folder/to/server', name='uploads')
app.url_for('static', name='bp.uploads', filename='file.txt') == '/bp/web/path/file.txt'

启停

蓝图可以在服务器的启动和停止过程中运行函数。如果在多处理器模式下运行(超过1个工作进程),则在工作进程复刻后触发。

可用事件包括:

  • before_server_start :在服务器开始接受连接之前执行

  • after_server_start :在服务器开始接受连接后执行

  • before_server_stop :在服务器停止接受连接之前执行

  • after_server_stop :在服务器停止且所有请求完成后执行

bp = Blueprint('my_blueprint')

@bp.listener('before_server_start')
async def setup_connection(app, loop):
    global database
    database = mysql.connect(host='127.0.0.1'...)

@bp.listener('after_server_stop')
async def close_connection(app, loop):
    await database.close()

用例:API版本控制

蓝图对于API版本控制非常有用,其中一个蓝图可以指向 /v1/<routes> ,另一个指向 /v2/<routes> .

当初始化蓝图时,可以选择 version 参数,该参数将位于蓝图上定义的所有路由的前面。此功能可用于实现我们的API版本控制方案。

# blueprints.py
from sanic.response import text
from sanic import Blueprint

blueprint_v1 = Blueprint('v1', url_prefix='/api', version="v1")
blueprint_v2 = Blueprint('v2', url_prefix='/api', version="v2")

@blueprint_v1.route('/')
async def api_v1_root(request):
    return text('Welcome to version 1 of our documentation')

@blueprint_v2.route('/')
async def api_v2_root(request):
    return text('Welcome to version 2 of our documentation')

当我们在应用程序上注册蓝图时 /v1/api/v2/api 现在将指向单个蓝图,它允许创建 sub-sites 对于每个API版本。

# main.py
from sanic import Sanic
from blueprints import blueprint_v1, blueprint_v2

app = Sanic(__name__)
app.blueprint(blueprint_v1)
app.blueprint(blueprint_v2)

app.run(host='0.0.0.0', port=8000, debug=True)

URL生成方式 url_for

如果希望为蓝图内的路由生成URL,请记住端点名称采用以下格式 <blueprint_name>.<handler_name> . 例如:

@blueprint_v1.route('/')
async def root(request):
    url = request.app.url_for('v1.post_handler', post_id=5) # --> '/v1/api/post/5'
    return redirect(url)


@blueprint_v1.route('/post/<post_id>')
async def post_handler(request, post_id):
    return text('Post {} in Blueprint V1'.format(post_id))