[python] pprika:基于werkzeug编写的web框架(5) ——蓝图blueprint

蓝图实例

from pprika import Blueprint

main = Blueprint('main', '/animals')


@main.route('/cat')
def get_meow():
    return '喵喵!!是猫咪'

蓝图的初始化需要提供至少一个参数

class Blueprint(object):
    def __init__(self, name, url_prefix=None):
        if url_prefix is None:
            url_prefix = '/' + name
        self.name = name
        self.url_prefix = url_prefix
        self._deferred_funcs = []

name为该蓝图的名称,是之后路由时的依据,需要每个蓝图都不一样。而url_prefix默认为name,它用于附加到path之前,划分不同蓝图的作用域?如上例所对应的请求url则为 http://xy.z/animals/cat。_deferred_funcs 的用途后面会提到。

蓝图的路由及请求处理几乎与PPrika类一致,如上述route注册方式等同于 main.add_url_rule('/cat', 'get_meow', get_meow) ,因此直接讲add_url_rule。

 

add_url_rule

    def add_url_rule(self, path, endpoint=None, view_func=None, **options):
        path = "/".join((self.url_prefix.rstrip("/"), path.lstrip("/")))

        if view_func and hasattr(view_func, "__name__"):
            assert (
                "." not in view_func.__name__
            ), '视图函数名不应带"."'

        if endpoint:
            assert "." not in endpoint, 'endpoint参数不可带"."'
        if endpoint is None:
            assert view_func is not None, "无endpoint时view_func不可为空"
            endpoint = view_func.__name__

        endpoint = f'{self.name}.{endpoint}'

上面这一大段其实只做了三件事:附加 url_prefix 到path前、确保视图函数名不能带点(dot)、将endpoint调整为 name.xx 的点分结构。因为blueprint的路由请求响应本质还是通过app进行的,它只是提供了代码更好的组织方式(还有相对独立的错误处理),所以只要处理好参数转发给app即可。 ↓

        # 函数通过deferred_funcs转发,等到有了app再执行(注册rule)
        self._deferred_funcs.append(
            lambda a: a.add_url_rule(path, endpoint, view_func, **options)
        )

最后用之前处理好的参数调用PPrika类的同名方法即可,但由于此时app未必存在,因此需要通过 self._deferred_funcs 先将该操作保存起来,等后面把blueprint注册到app上时再调用。

同样的,blueprint上的error_handler等也是这样,最终都将注册操作暂存到_deferred_funcs 上,等app出现再转发给它,这里不多赘述。

 

register

    def register(self, app):
        for f in self._deferred_funcs:
            f(app)

比起flask,pprika的蓝图注册就只是调用之前暂存的那些lambda函数,省去了static、template等不少处理,而且由于省去了flask.BlueprintSetupState这一中间层,url_prefix在蓝图对象实例化后就不可再变。

而这个register函数又由PPrika.register_blueprint调用↓

 

PPrika.register_blueprint

    def register_blueprint(self, blueprint):
        bp_name = blueprint.name
        if bp_name in self.blueprints:
            assert blueprint is self.blueprints[bp_name], f"""
                已存在名为 {bp_name} 的blueprints:{self.blueprints[bp_name]},
                请确保正在创建的 {blueprint} 名称唯一
            """
        else:
            self.blueprints[bp_name] = blueprint
        blueprint.register(self)

做了两件事:根据app.blueprints保存的蓝图名与蓝图对象映射确保每个蓝图名称都唯一,在此基础上将这次注册的蓝图记入app.blueprints并调用蓝图上的register完成后续注册过程。

这样一个蓝图对象就成功注册到app上了,之后蓝图上的请求、错误实质都会用app上之前介绍的那些函数处理,与一般app注册的视图没什么差别。

 

结语

本篇的blueprint.Blueprint是之后restful.Api实现的基础,可以说Api就是一个增强的Blueprint

之后预计由两篇讲述restful功能的实现

[python] pprika:基于werkzeug编写的web框架(6) ——restful的错误处理

 

posted @ 2020-05-31 21:40  NoNoe  阅读(187)  评论(0编辑  收藏  举报