zoukankan      html  css  js  c++  java
  • [python] pprika:基于werkzeug编写的web框架(2) ——路由与请求响应

    最简单的示例

    from pprika import PPrika
    
    app = PPrika()
    
    
    @app.route('/')
    def index():
        return 'Hello world!'
    
    
    if __name__ == '__main__':
        app.run()

    可以看到用法与flask几乎一样,但app生成不需要传递import_name

    下方开始按请求处理顺序介绍PPrika类的方法

    route

    它是 add_url_rule 的装饰器版本

    等同于 app.add_url_rule('/', view_func=index)

    add_url_rule

    def add_url_rule(self, path, endpoint=None, view_func=None, **options):
        if endpoint is None:
            assert view_func is not None, "无endpoint时view_func不可为空"
            endpoint = view_func.__name__

    类似于flask的同名函数 `add_url_rule`,它将一个url注册到给定的endpoint上,并关联endpoint与视图函数view_func。其中url与endpoint多对一,endpoint与view_func一对一,通过endpoint作中介实现了url与func多对一映射。

    参数中path即为要绑定的url路径,endpoint若省略则将视图函数名作为其值,因此二者不可同时为空。

        methods = options.pop('methods', '') or ("GET",)
        if isinstance(methods, str):
            # 即允许了类似methods="POST"的method指定方式
            methods = (methods,)
        methods = set(item.upper() for item in methods)

    从options中尝试获取该视图函数所对应的methods,但即便没有显式 'HEAD' 方法,在路由时werkzeug会将HEAD请求作为GET匹配。

        rule = Rule(path, methods=methods, endpoint=endpoint, **options)
        self.url_map.add(rule)

    利用之前处理好的参数构造Rule对象,并将其加入Map。

    其中 self.url_map 是Map的实例,Map与Rule都是  werkzeug.routing 提供的类。

    通过这一步就完成了一个路由关系的记录,是整个 add_url_rule 的核心步骤

        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError("此endpoint已有对应函数: %s" % endpoint)
            self.view_functions[endpoint] = view_func

    上一步是在url与endpoint之间建立联系,而这一步就是在endpoint与view_func之间产生关联,并将view_func记录于self.view_functions这一字典中。除此之外还会判断endpoint是否已有对应的视图函数,这部分来自于flask,说实在不是很明白它的意义。

    推测:当执行add_url_rule('/', 'index', index)时endpoint='index',绑定了index函数,这时若再执行add_url_rule('/home', 'index', func)要求 func == index,否则会违背endpoint与view_func一对一的原则。

    run

    from werkzeug.serving import run_simple
    
    def run(self, host='localhost', port=9000, **options):
        options.setdefault("threaded", True)  # 线程隔离
        run_simple(host, port, self, **options)

    本质上是将参数传递给werkzeug提供的开发服务器

    其中 self 指的是PPrika的实例,即上方的app。run_simple运行后会等待请求到来,当收到请求时会将所有的请求参数整理为一个变量传递给app,将其作为函数调用得到视图函数返回值,因此需要app实现 __call__ 方法。

    ps: 一开始开放的是6000端口,测试了很久发现chrome居然拒绝发出请求,而Edge运行正常。

    __call__

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    __call__仅负责转发请求给wsgi_app。这里的environ、start_response由run_simple提供,environ包含了所有请求信息,而start_response则是请求处理最后返回响应时调用的函数,但无需手动使用它。

    wsgi_app

    假设此时一个请求(http://localhost:9000/)到来,那么run_simple就会将请求传递给__call__,再进一步交由wsgi_app处理。赛这里会实现路由的匹配、处理的请求、响应结果返回以及异常的捕捉与处理。

    def wsgi_app(self, environ, start_response):
        ctx = RequestContext(self, environ)  # 请求上下文对象
        try:
            try:
                ctx.bind()  # 绑定请求上下文并匹配路由
                rv = self.dispatch_request()
                response = make_response(rv)
            except Exception as e:
                response = self.handle_exception(e)
            return response(environ, start_response)
        finally:
            ctx.unbind()

    ctx是请求上下文对象先不用在意,仅需知道通过ctx.bind全局变量request将变得可用。

    通过ctx.bind匹配路由后wsgi_app会尝试调用dispatch_request去调用该路由相应的视图函数并获取函数返回值rv,并通过make_response生成响应对象,由于该对象是 werkzeug.wrappers.Response 的实例,以environ与start_response调用它可得到最终的响应,至此整个请求处理完成。

    handle_exception作为外层处理函数负责处理没有绑定错误处理器的错误或其他错误。

    ps:对比flask,由于pprika没有实现请求钩子之类的,因此也不需要额外的一层full_dispatch_request

    dispatch_request

    def dispatch_request(self):
        if request.routing_exception is not None:
            return self.handle_user_exception(request.routing_exception)
        # 'url_adapter.match' 时可能产生的路由错误
    
        try:
            endpoint, args = request.rule.endpoint, request.view_args
            rv = self.view_functions[endpoint](**args)
        except Exception as e:
            rv = self.handle_user_exception(e)
        return rv

    request.routing_exception是上文中ctx.bind时可能发生的路由错误

    dispatch_request会尝试从request中获取 endpoint, args,通过endpoint得到对应视图函数并将args作为参数传入得到函数返回值rv。

    假如一切正常,会将rv返回给上文的wsgi_app,流程顺利结束。

    若视图函数中抛出了异常会先以 self.handle_user_exception 尝试处理,若无法处理则会再次将错误抛出交给wsgi_app中提及的handle_exception。

    结语

    至此整个路由、请求接受与响应的过程都介绍完毕,这些是框架最核心的功能。

    下一篇将讲述错误处理、请求上下文与帮助函数。

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

  • 相关阅读:
    robotframework +selenium 自动化测试之浏览器与驱动的兼容问题。
    robotframework+selenium自动化robotramework版本问题
    一个业务逻辑引发的对多表连接的思考
    第一次接触WebSocket遇到的坑以及感受
    关于读写APP.config文件能读却写不了的问题
    poj2392 space elevator
    洛谷P1197 星球大战
    poj3421&poj3292&poj2689 基础数论
    洛谷P1006 传纸条
    挑战程序设计竞赛2.3节习题选解
  • 原文地址:https://www.cnblogs.com/Stareven233/p/12965668.html
Copyright © 2011-2022 走看看