引言

本文主要梳理了flask源码中route的设计思路。

首先,从WSGI协议的角度介绍flask route的作用;

其次,详细讲解如何借助werkzeug库的MapRule实现route

最后,梳理了一次完整的http请求中route的完整流程。

flask route 设计思路

源码版本说明

本文参考的是flask 0.5版本的代码。

flask 0.1版本的代码非常短,只有600多行,但是这个版本缺少blueprint机制。

因此,我参考的是0.5版本。

flask route示例

直接使用flask官方文档中的例子

from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
    return 'Hello World!'
@app.route('/post/')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id
if __name__ == '__main__':
    app.run()

此例中,使用app.route装饰器,完成了以下两个url与处理函数的route:

{ 
    '/': hello_world, 
    '/post/' : show_post
}

这样做的效果为:
当http请求的url为'/'时,flask会调用hello_world函数;
当http请求的url为'/post/<某整数值>'(例如/post/32)时,flask会调用show_post函数;

flask route的作用

从上面的示例中其实可以明白:flask route的作用就是建立url与处理函数的映射

WSGI协议将处理请求的组件按照功能及调用关系分成了三种:server, middleware, application

其中,server可以调用middleware和application,middleware可以调用application。

符合WSGI的框架对于一次http请求的完整处理过程为:

server读取解析请求,生成environ和start_response,然后调用middleware;

middleware完成自己的处理部分后,可以继续调用下一个middleware或application,形成一个完整的请求链;

application位于请求链的最后一级,其作用就是生成最终的响应。

 http服务器(比如,nginx)--&gt; WSGI server(比如gunicorn,SimpleHttpServer)--&gt;middleware--&gt;
 middleware--&gt; ... --&gt;application

如果接触过Java Web 开发的人可能会立刻发现,这与servlet中的middleware机制是完全一致的。

特别重要的:

在上一小节的示例中app = Flask(__name__)创建了一个middleware
而这个middleware的核心作用是进行请求转发(request dispatch)。

上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。

进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。
因此,在flask中,route是Flask类的一个装饰器。

flask route的实现思路

通过上一小节,我们知道以下两点:

  • flask route 是url与处理函数的映射关系;
  • 在http请求时,Flask这个middleware负责完成对url对应的处理函数的调用;

那么,如果是我们自己来实现route,思路也很简单:

  • 建立一个类Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map
  • url_map = {url : function}
  • 当http请求时,进行request dispatch:根据url,从url_map中找到function,然后调用function;
  • 调用后续的middleware或application,并把function的结果传递下去。

flask的实现思路也是这样的。

class Flask(object):
    def __init__(self):
        self.url_map = {}  # 此处定义保存url与处理函数的映射关系
    def __call__(self, environ, start_response):  # 根据WSGI协议,middleware必须是可调用对象
        self.dispatch_request()    # Flask的核心功能 request dispatch
        return application(environ, start_response)  #最后调用下一级的application
    def route(self, rule):  # Flask使用装饰器来完成url与处理函数的映射关系建立
        def decorator(f):   # 简单,侵入小,优雅
            self.url_map[rule] = f
            return f
        return decorator
    def dispath_request(self):
        url = get_url_from_environ() #解析environ获得url 
        return self.url_map[url]() #从url_map中找到对应的处理函数,并调用

至此, 一个简单的Flaskmiddleware的骨架就完成了。
上面的Flask类主要功能包括:

  • 符合WSGI协议的middleware:可被调用,并且可以调用application
  • 能够保存url与处理函数的映射信息
  • 能够根据url找到处理函数并调用(即,request dispatch)

当然,在实际中,不可能这么简单,但是基本思路是一致的。

werkzeug库中的Map与Rule在Flask中的应用

需要指出,上面实现的最简单的Flask类还是有很多问题的。
比如,HTTP请求中相同的url,不同的请求方法,比如GET,POST如果对应不同的处理函数,该如何处理?

flask使用了werkzeug库中的MapRule来管理url与处理函数映射关系。

首先需要简单了解一下MapRule的作用:

werkzeug中,Rule的主要作用是保存了一组urlendpointmethods关系:

每个(url, endpoint, methods)都有一个对应的Rule对象:

其实现如下:

class Rule(object):
    def __init__(self, url, endpoint, methods):
        self.rule = url
        self.endpoint = endpoint
        self.methods = methods

这里需要解释一下endpoint

前面说过:url与其处理函数可以使用一个字典来实现:{url: function}

flask在实现的时候,在中间加了一个中介endpoint,于是,url与处理函数的映射变成了这样:

url-->endpoint-->function #一个url对应一个endpoint,一个endpoint对应一个function
{url: endpoint} # 保存url与endpoint之间的关系
{endpoint: function} #保存endpoint与function之间的关系

于是,刚才我们实现的简单的flask骨架中{url: function}的字典,就变成了{endpoint: function},而{url: endpoint}这个映射关系就需要借助MapRule这两个类来完成。

可以发现:endpoint就是url和处理函数映射关系中的一个中介,所以,它可以是任何可以用作字典键的值,比如字符串。

但是在实际使用中endpoint,一般endpoint均为字符串,并且默认情况下:

  • 如果是通过Flask.route装饰器建立的映射关系,那么endpoint就是处理函数的函数名;
  • 如果是通过blueprint建立的映射关系,那么endpoint是blueprint名.处理函数名;

因为,每建立一个url-->endpoint-->function关系就会创建一个Rule对象,所以,会有很多Rule对象存在。
 

Map的作用则是保存所有Rule对象。

所以,一般情况下Map的用法如下:

    m = Map([
            Rule('/', endpoint='index'),
            Rule('/downloads/', endpoint='downloads/index'),
            Rule('/downloads/', endpoint='downloads/show')
           ])

在flask的源码中

class Flask(object):
    def __init__(self):
        self.url_map = Map()  # url_map为保存所有Rule关系的容器Map
        self.view_functions = {} # view_functions保存endpoint--&gt;function
  • 成员变量url_map保存所有的(url, endpoint, method)关系
  • 成员变量view_functions保存所有的{endpoint, function}关系

所以,对于一个url,只要能找到(url,endpoint,method),就能根据endpoint找到对应的function

route的完整流程

首先,建立Flask对象:

app = Flask(__name__)

然后,建立urlfunction之间的映射关系:

@app.route('/')
def hello_world():
    return 'Hello World!'

在装饰器route中,创建(url, endpoint, method){endpoint: function}两组映射关系:

if endpoint is None:
    endpoint = view_func.__name__ # 默认使用响应函数名作为endpoint
self.url_map.add(Rule(url, endpoint, method)) # 保存(url, endpoint, method)映射关系
self.view_functions[endpoint] = view_func  # 保存{endpoint: function}映射关系

这样,就完成了对url和响应函数的映射关系。

下一步,调用WSGI server响应http请求,在文章开始的示例中使用:

app.run()

调用python标准库提供的WSGI server,在实际使用时,可能是gunicornuwsgi

不论server是什么,最终都会调用Flask.__call__函数。这个函数完成request dispatch的任务。

对于request dispatch而言,首先根据请求,解析environ,得到url,然后调用Map.match函数,这个函数会最终找到预先保存的(url, endpoint, method)映射,然后返回(endpoint, url请求参数),由于得到了endpoint,然后,可以从Flask.view_functions中直接取到对应的响应函数,所以,可以直接进行函数调用

self.view_functions[endpoint](url请求参数)

至此,就完成了完整的route

总结

flaskFlask类是WSGIdispatch middleware

Flaskurl_map保存所有的(url, endpoint, method)映射关系;

Flaskview_functions保存所有的{endpoint: function}映射关系;

dispath request就是根据url找到endpoint,再根据endpoint找到function,最后调用function的过程

以上就是flask route对协议作用及设计思路的详细内容,更多关于flask route协议设计的资料请关注Devmax其它相关文章!

flask route对协议作用及设计思路的更多相关文章

  1. 使用Retrofit在Android中重新创建flask api调用

    我在服务器上有一个烧瓶app和api,它使用从终端发送的以下url我试图在Android上使用改造来重新创建它.我使用的是1.7版,因为这适用于此处未显示的一些遗留代码.这是应用程序类的相关部分和api类我现在只得到一般性错误,例如这是我的第一个烧瓶应用程序,我不完全确定如何调试所以任何帮助在这里也是赞赏.我也没有访问服务器日志更新为了尝试追踪问题,我编辑了服务器上的代码.如果我只是在api中返回

  2. Flask-Vue前后端分离的全过程讲解

    这篇文章主要介绍了Flask-Vue前后端分离的全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. Java设计模式中的装饰者模式

    这篇文章主要介绍了Java设计模式中的装饰者模式,装饰者模式即Decorator Pattern,装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能

  4. Java+MySQL实现设计优惠券系统

    这篇文章主要介绍了Java+MySQL实现设计优惠券系统,文章基于Java与MySQL的相关资料展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

  5. Python Flask框架开发之运用SocketIO实现WebSSH方法详解

    Socket.IO本是一个面向实时web应用的JavaScript库,现在已成为拥有众多语言支持的Web即时通讯应用的框架。这篇文章主要介绍了Python 运用SocketIO实现WebSSH方法

  6. Java设计模式中的桥接模式

    这篇文章主要介绍了Java设计模式中的桥接模式,其是一种结构型设计模式,是指将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变

  7. iOS9苹果将原http协议改成了https协议的方法

    这篇文章主要介绍了iOS9苹果将原http协议改成了https协议的方法的相关资料,需要的朋友可以参考下

  8. Flask项目的部署的实现步骤

    本文主要介绍了Flask项目的部署的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  9. Java有趣好玩的图形界面开发八个案例实现

    今天使用GUI技术写了几个练习的Demo,希望对大家学习图形用户界面有所帮助,感兴趣的同学来看看吧,动手敲一遍理解更通透

  10. Elasticsearches之python使用及Django与Flask集成示例

    这篇文章主要为大家介绍了Elasticsearches之python使用及Django与Flask集成示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

随机推荐

  1. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  2. python数学建模之三大模型与十大常用算法详情

    这篇文章主要介绍了python数学建模之三大模型与十大常用算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感想取得小伙伴可以参考一下

  3. Python爬取奶茶店数据分析哪家最好喝以及性价比

    这篇文章主要介绍了用Python告诉你奶茶哪家最好喝性价比最高,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  4. 使用pyinstaller打包.exe文件的详细教程

    PyInstaller是一个跨平台的Python应用打包工具,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,下面这篇文章主要给大家介绍了关于使用pyinstaller打包.exe文件的相关资料,需要的朋友可以参考下

  5. 基于Python实现射击小游戏的制作

    这篇文章主要介绍了如何利用Python制作一个自己专属的第一人称射击小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起动手试一试

  6. Python list append方法之给列表追加元素

    这篇文章主要介绍了Python list append方法如何给列表追加元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  7. Pytest+Request+Allure+Jenkins实现接口自动化

    这篇文章介绍了Pytest+Request+Allure+Jenkins实现接口自动化的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. 利用python实现简单的情感分析实例教程

    商品评论挖掘、电影推荐、股市预测……情感分析大有用武之地,下面这篇文章主要给大家介绍了关于利用python实现简单的情感分析的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

  9. 利用Python上传日志并监控告警的方法详解

    这篇文章将详细为大家介绍如何通过阿里云日志服务搭建一套通过Python上传日志、配置日志告警的监控服务,感兴趣的小伙伴可以了解一下

  10. Pycharm中运行程序在Python console中执行,不是直接Run问题

    这篇文章主要介绍了Pycharm中运行程序在Python console中执行,不是直接Run问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

返回
顶部