引言

本文主要梳理了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/<int:post_id>')
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/<int:post_id>' : 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)--> WSGI server(比如gunicorn,SimpleHttpServer)-->middleware-->
middleware--> ... -->application

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

特别重要的:

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

上面这句话非常重要,请在心里重复一百遍。
进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。

因此,在flask中,route是Flask类的一个装饰器。

flask route的实现思路

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

  1. flask route 是url与处理函数的映射关系;

  2. 在http请求时,Flask这个middleware负责完成对url对应的处理函数的调用;

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

  1. 建立一个类Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map

  2. url_map = {url : function}

  3. 当http请求时,进行request dispatch:根据url,从url_map中找到function,然后调用function;

  4. 调用后续的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类主要功能包括:

  1. 符合WSGI协议的middleware:可被调用,并且可以调用application

  2. 能够保存url与处理函数的映射信息

  3. 能够根据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均为字符串,并且默认情况下:

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

  2. 如果是通过blueprint建立的映射关系,那么endpoint是blueprint名.处理函数名;

因为,每建立一个url-->endpoint-->function关系就会创建一个Rule对象,所以,会有很多Rule对象存在。
Map的作用则是保存所有Rule对象。
所以,一般情况下Map的用法如下:

    m = Map([
Rule('/', endpoint='index'),
Rule('/downloads/', endpoint='downloads/index'),
Rule('/downloads/<int:id>', endpoint='downloads/show')
])

在flask的源码中

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

  2. 成员变量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

总结

  1. flaskFlask类是WSGIdispatch middleware

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

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

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

https://segmentfault.com/a/1190000004213652

Ubuntu下SSH安装及提高SSH登陆认证速度的办法的更多相关文章

  1. ubuntu下git安装及使用

    ubuntu下git安装及使用   其实,好几个月前,就已经安装好了,可是一直搁置在那儿,所以密码等一些其它细节都忘的差不多了,所以今天就重新部署了一下,并开始积极使用......... 1,git ...

  2. Torch7在Ubuntu下的安装与配置

    Torch7的本系列教程的主要目的是介绍Torch的入门使用.今天首先分享一下Torch7的安装.(在Ubuntu14.04安装torch7) 为什么选择Torch Torch的目标是在建立科学算法的 ...

  3. premake Ubuntu下的安装

    premake是个跨平台的编译工具,先看看在Ubuntu下怎么安装. 首先下载,在/usr目录下: sudo wget -O premake-4.4-beta4-linux.tar.gz http:/ ...

  4. linux,windows,ubuntu下git安装与使用

    ubuntu下git安装与使用:首先应该检查本地是否已经安装了git ,如果没有安装的话,在命令模式下输入 sudo apt-get install git 进行安装 输入git命令查看安装状态及常用 ...

  5. ubuntu下如何安装codeblocks集成开发环境

    codeblocks是一个十分优秀的C/C++开发IDE,虽然后起之秀codelite目前来看大有超越之势哦. 不过在ubuntu下安装codeblocks却比较麻烦,不像其他linux发行版,比如s ...

  6. ubuntu下makeinfo安装,其实真正安装的是texinfo包

    操作系统环境:ubuntu 在终端中执行命令:sudo apt-get install texinfo   今天在打包的时候有个包需要 makeinfo,当时就各种搜结果就没有 makeinfo 这个 ...

  7. Ubuntu下软件安装方式、PATH配置、查找安装位置

    Ubuntu 18.04, 安装方式 目前孤知道的Ubuntu下安装软件方式有3种(命令): 1.make 2.apt/apt-get 3.dpkg 方式1基于软件源码安装,需要经历配置(可选).编译 ...

  8. Mac下新安装的MySQL无法登陆root用户解决方法

      一 设置MySQL命令行搜索路径 0.苹果->系统偏好设置->最下边点mysql 在弹出页面中 启动mysql服务 1.打开终端,输入: sudo vi ~/.bash_profile ...

  9. ubuntu 下python安装及hello world

    //@desn:ubuntu 下python安装及hello world //@desn:码字不宜,转载请注明出处 //@author:张慧源  <turing_zhy@163.com> ...

随机推荐

  1. word使用宏定义来统一设置图片大小

    1. 首先手动拖拽将图片调到需要的格式,点击图片在格式选项中查看图片的宽高 2. 视图中点击宏新建 3. 编辑框中输入以下代码并保存,由于我只需要统一宽度,所以将统一高度的代码注释 Sub 图片格式统 ...

  2. PHP中一些有用的函数

    <?php /** * 加密解密 * * @param string $key * @param string $string * @param string $decrypt * @retur ...

  3. HDUOJ---2642Stars(二维树状数组)

    Stars Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/65536 K (Java/Others)Total Submi ...

  4. HDUOJ------(1272)小希的迷宫

    小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  5. RabbitMQ概念及环境搭建(三)RabbitMQ cluster

    测试环境:VMS00781 VMS00782 VMS00386 (centos5.8) 1.先在三台机器上分别安装RabbitMQ Server 2.读取其中一个节点的cookie,并复制到其他节点( ...

  6. win7 下jenkins配置与使用

    一.Jenkins的配置 1. 下载地址: Jenkins的官方网站:http://jenkins-ci.org/ 目前最新版本的Windows版:http://mirror.xmission.com ...

  7. [DLX] hust 1017 Exact cover

    题意: 给你N个包,要拿到M个东西(编号1~M每一个仅仅能有一个) 然后每一个包里有k个东西,每一个东西都有编号. 思路: 舞蹈连模板题 代码: #include"stdio.h" ...

  8. [hihoCoder] 骨牌覆盖问题·一

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 骨牌,一种古老的玩具.今天我们要研究的是骨牌的覆盖问题:我们有一个2xN的长条形棋盘,然后用1x2的骨牌去覆盖整个棋盘.对 ...

  9. vim自定义插件放入pathogen管理

    1.在.vim/bundle目录下,建立一个空目录,比如cscope 2.在cscope下面建立一个plugin文件夹 3.将自己写的vim文件放入plugin文件夹内就可以使用.

  10. javascript中字符串与数组互转的方法分享

    说明:1.join()方法:用于把数组中的所有元素放入一个字符串,元素是通过指定的分隔符进行分隔的.指定分隔符方法join("$");其中$可以是任意字符2.split()方法:用 ...