flask route对协议作用及设计思路

目录
  • 引言
  • flask route 设计思路
    • 源码版本说明
    • flask route示例
    • flask route的作用
    • flask route的实现思路
    • werkzeug库中的Map与Rule在Flask中的应用
    • route的完整流程
  • 总结

引言

本文主要梳理了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协议设计的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flask 的路由Route详情

    目录 1.路由 2.视图函数绑定多个url 3.动态url 4.HTTP请求方法设置 5.url构建 前言: 在上一篇Flask 入门Web 微框架Hello Flask中,我们用 Flask 框架写了一个 Hello Flask 应用程序,我们了解到 Flask 框架简洁高效.可以快速上手,接下来将对 Flask 框架的各项功能详细的介绍一下,本篇文章介绍的是 Flask 的路由(Route). 1.路由 所谓路由,就是处理请求url和函数之间关系的程序,一个Web应用不同的路径会有不同的处理

  • Flask框架URL管理操作示例【基于@app.route】

    本文实例讲述了Flask框架URL管理操作.分享给大家供大家参考,具体如下: 动态URL规则 URL规则可以添加变量部分,也就是将符合同种规则的URL抽象成一个URL模式,如"/item/1/","/item2/",假如不抽象,我们就得这样写: @app.route("/item/1/") 动态URL写法: @app.route("/item1/<id>/") def item1(id): pass 尖括号的内容是

  • Python的Flask框架中@app.route的用法教程

    在我上一篇文章,我搭了一个框架,模拟了Flask网站上"@app.route('/')"第一条例子的行为. 如果你错过了那篇"这不是魔法",请点击这里. 在这篇文章中,我们打算稍微调高点难度,为我们的URL加入可变参数的能力,在本文的最后,我们将支持下述代码段所期望达到的行为. app = Flask(__name__) @app.route("/hello/<username>") def hello_user(username):

  • flask route对协议作用及设计思路

    目录 引言 flask route 设计思路 源码版本说明 flask route示例 flask route的作用 flask route的实现思路 werkzeug库中的Map与Rule在Flask中的应用 route的完整流程 总结 引言 本文主要梳理了flask源码中route的设计思路. 首先,从WSGI协议的角度介绍flask route的作用: 其次,详细讲解如何借助werkzeug库的Map.Rule实现route: 最后,梳理了一次完整的http请求中route的完整流程. f

  • Go 微服务开发框架DMicro设计思路详解

    目录 背景 概述 架构 设计理念 面向接口设计 会话 Session 消息 Message 协议 Proto 编码 Codec 连接 Socket 有机的组合 插件 Plugin 组件 未来展望 背景 DMicro 诞生的背景,是因为我写了 10 来年的 PHP,想在公司内部推广 Go, 公司内部的组件及 rpc 协议都是基于 swoole 定制化开发的.调研了市面上的各种框架,包括 beego,goframe,gin,go-micro,go-zero,erpc 等等,可能是我当时技术能力有限,

  • Flask与SMTP协议邮件扩展问题

    目录 Flask与SMTP协议邮件扩展 设置邮箱授权码 Flask框架(flask中的邮件发送Flask-Mail(邮件扩展)) 总结 Flask与SMTP协议邮件扩展 在开发过程中,很多应用程序都需要通过邮件提醒用户,Flask的扩展包Flask-Mail通过包装了Python内置的smtplib包,可以用在Flask程序中发送邮件. Flask-Mail连接到简单邮件协议(Simple Mail Transfer Protocol,SMTP)服务器,并把邮件交给服务器发送. 设置邮箱授权码

  • 网上考试设计思路是怎样的?

    global.asa < script LANGUAGE=VBScript RUNAT=Server > Sub Application_OnStart dbPath = "DBQ=" & Server.Mappath("onlinetest.mdb") dbConnectionString =  "DRIVER={Microsoft Access Driver (*.mdb)}; " & dbPath Set App

  • Flex实现双轴组合图的设计思路及代码

    1.设计思路 (1)设计一个组合图,该图共用一个数据源 (2)组合图是有柱状图和折线图组合的 (3)柱状图显示的数据是依据左边的竖直轴,折线图的数据依据右边的竖直轴 2.源码如下 DoubleY.mxml: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:

  • 使用JavaScript实现网页版Pongo设计思路及源代码分享

    1.游戏背景介绍(写在前面的废话): 五月初的某天,看到某网推荐了这款游戏,Pongo,看着还不错的样子就用ipad下下来试玩了下,玩了两局感觉还错挺过瘾的,因为是手欠类游戏嘛大家懂的. 但是没一会发现游戏在ipad似乎有些bug,玩一会就会卡住然后只能强退了,真是揪心,记录还等着破呢. 怎么办?玩游戏不如玩自己的游戏的念头又邪恶的出现了,然后就把pad丢给了朋友虐心去,我默默回到电脑前开始动手自己写个不会卡的. 大概两小时吧,写出了基本框架,然后扔sinaapp里试了下效果基本能玩就洗洗睡了.

  • MySQL备份恢复设计思路

    背景 首先交代一下背景,由于某些因素的限制,我们公司目前的备份策略采用的是隔天全备的方案,增量备份则使用的是binlog server的方式,那么如何快速恢复就成为了我们需要思考的问题 恢复需求 根据我以往的一些经验来说,通常需要从备份恢复数据的场景有如下几种: 1.被误删库了 2.被误删表了,类型为TRUNCATE或者DROP 3.被误删列了,类型为ALTER ... DROP COLUMN 4.被误删数据了,类型为DELETE或者UPDATE或者REPLACE 5.表空间损坏或出现坏块了 根

  • Docker容器定时备份数据库并发送到指定邮箱(设计思路)

    目录 一.背景: 二.设计思路: 三.编写启动脚本 四.构建镜像 五.添加边车容器 5.1 创建配置文件 5.2 创建有状态服务部署文件 六.测试 项目地址及使用说明:https://gitee.com/noovertime/mysqlmail 一.背景: 一开始的初衷是,想写一个脚本来监控服务器的高占用率进程并通过邮件发送给我,然后突发奇想,可以使用这种方式来备份我的数据库,开始动手! 二.设计思路: 通过编写shell脚本,调用linux的mail工具,mysqldump的方式来保存数据库的

  • 基于SpringBoot实现自动装配返回属性的设计思路

    目录 一:需求背景 二:设计思路 三:使用方法 四:注解解析器(核心代码) 五:需要思考的技术点 一:需求背景 在业务开发中经常会有这个一个场景,A(业务表)表中会记录数据的创建人,通常我们会用userId字段记录该数据的创建者,但数据的使用方会要求展示该数据的创建者姓名,故我们会关联用户表拿该用户的姓名.还有一些枚举值的含义也要展示给前端.导致原本一个单表的sql就要写成多表的关联sql,以及枚举含义的转换很是恶心. 例如:业务对象BusinessEntity.java public clas

  • vuex 设计思路和实现方式

    目录 vuex 设计思路和实现 vuex 设计思路 vue 响应式设计,依赖监听.依赖收集 vuex 设计思路和实现 API概念的东西就不介绍了, 如果还不了解vuex 的应用, 可以去查看官方vuex文档 . 下面着重讲解 vuex的原理以及实现 vuex 设计思路 vuex是使用插件机制开发的, vuex 中的 store 本质就是没有template的隐藏着的vue实例 在beforeCreate 混入vuexInit ,vuexInit方法实现了store注入vue组件实例,并注册了vu

随机推荐