多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 蓝图 蓝图概念来自**Flask**,意思是可在应用中,用来做子路由的对象。相对于前面章节讲到的增加路由方式,蓝图也定义了类似的方法,但采用插件方式更易于扩展。在大型应用中,蓝图尤其有用,能方便地把服务解耦成各自独立的模块。 ## 第一个蓝图 下面示范了一个简单的蓝图,注册了访问`/`路由的handler。假设蓝图文件为`my_blueprint.py`,后面需要`import`到主应用中。 ```python from sanic import response, Blueprint bp = Blueprint('my_blueprint') @bp.route('/') async def bp_root(request): return response.json({'my': 'blueprint'}) ``` ## 注册蓝图 蓝图需要注册到主程序中。 ```python from sanic import Sanic import my_blueprint app = Sanic(__name__) app.blueprint(my_blueprint.bp) app.run(host='0.0.0.0', port=8000, debug=True) ``` ## 使用蓝图 使用方式如同前面章节描述的主应用实例,一样的方式添加中间件,添加路由。 ### 中间件 同样可以注册全局中间件。 ```python @bp.middleware('request') async def print_bp_request(request): print('blue request middle') @bp.middleware('response') async def print_bp_reponse(request, response): print('blue response middle') ``` 也可以注册视图内的中间件(详见[视图](./class_based_views.md)) ```python class TestView(views.HTTPMethodView): @staticmethod @bp.middleware('request') async def print_bp_view_request(request): print('blue view request middle') @staticmethod @bp.middleware('response') async def print_bp_view_response(request, response): print('blue view response middle') async def get(self, request): return response.json({'type': 'view class'}) bp.add_route(TestView.as_view(), '/view') ``` ### 异常 蓝图内可以定义自己的异常。 ```python @bp.exception(NotFound) def ignore_404s(request, exception): return text("Yep, I totally found the page: {}".format(request.url)) ``` ### 开始和结束 蓝图也可以像主应用一样使用监听器。如果运行在多进程模式下,会在某个进城创建后触发该事件。可用的事件如下,事件触发方式与主应用一致: - before_server_start - after_server_start - before_server_stop - after_server_stop ```python 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>`。当蓝图创建时,可以使用`url_prefix`参数,用来指名当前蓝图下所有路由的前缀。这个特性可以用来实现API变换。 ```python from sanic import response, Blueprint blueprint_v1 = Blueprint('v1', url_prefix='/v1') blueprint_v2 = Blueprint('v2', url_prefix='/v2') @blueprint_v1.route('/') async def api_v1_root(request): return response.text('Welcome to version 1 of our documentation') @blueprint_v2.route('/') async def api_v2_root(request): return response.text('Welcome to version 2 of our documentation') ``` 当在应用中注册蓝图后,`/v1`和`/v2`路由会分别指向特定的蓝图,可以用来给不同的子站点应用对应的API。 ```python # main.py from sanic import Sanic from blueprints import blueprint_v1, blueprint_v2 app = Sanic(__name__) app.blueprint(blueprint_v1, url_prefix='/v1') app.blueprint(blueprint_v2, url_prefix='/v2') app.run(host='0.0.0.0', port=8000, debug=True) ```` 如果main.py的注册程序和my_blurprint.py初始化中都有url_prefix参数,那么main的会覆盖掉Blueprint初始化的设定。app.py源码中也可以证明 ```python def blueprint(self, blueprint, **options): if blueprint.name in self.blueprints: assert self.blueprints[blueprint.name] is blueprint, \ 'A blueprint with the name "%s" is already registered. ' \ 'Blueprint names must be unique.' % \ (blueprint.name,) else: self.blueprints[blueprint.name] = blueprint self._blueprint_order.append(blueprint) # 覆盖掉blurprint实例的设定 blueprint.register(self, options) ``` ### 用`url_for`创建URL 如果需要在蓝图中创建路由,记得端点格式是`<blueprint_name>.<handler_name>`,例如: ```python @blueprint_v1.route('/') async def root(request): url = request.app.url_for('v1.post_handler', post_id=5) # --> '/v1/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)) ```