源码解析flask的路由系统

当我们新建一个flask项目时,pycharm通常已经为项目定义了一个基本路由

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

此时在浏览器中输入地址http://127.0.0.1:5000,页面会显示出"Hello World!"的字样

如下图所示

那么此时在flask后台程序中,到底发生了什么事情呢??

在上面的例子中,可以看到对hello_world视图函数被app.route这个有参装假器装饰

来看下app.route这个有参装饰器的内部实现原理

app是Flask主程序的类实例化本项目名得到的一个对象

app = Flask(__name__)

然后调用app对象的route方法来装饰hello_world视图函数

route方法的源码:

def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop(‘endpoint‘, None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator

在用app.route装饰hello_world视图函数的时候,实际上app.route中还可以添加一些参数。

比如指定请求的方法的变量:methods=["GET","POST"]以及指定视图函数的endpoint,相当于Django中视图函数的别名等

在这里,rule参数相当于hello_world视图函数中的"/"路径,options参数中包含methods和endpoint等

在route装饰器里,返回decorator闭包函数。

在decorator闭包函数中,先从options中获取endpoint的值,endpoint的值默认为None

然后调用self.add_url_rule内部方法处理传递的参数rule,endpoint,f等,在这里self指的是app这个对象

查看app对象中的add_url_rule方法:

    @setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):

        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options[‘endpoint‘] = endpoint
        methods = options.pop(‘methods‘, None)

        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            methods = getattr(view_func, ‘methods‘, None) or (‘GET‘,)
        if isinstance(methods, string_types):
            raise TypeError(‘Allowed methods have to be iterables of strings, ‘
                            ‘for example: @app.route(..., methods=["POST"])‘)
        methods = set(item.upper() for item in methods)

        # Methods that should always be added
        required_methods = set(getattr(view_func, ‘required_methods‘, ()))

        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        provide_automatic_options = getattr(view_func,
            ‘provide_automatic_options‘, None)

        if provide_automatic_options is None:
            if ‘OPTIONS‘ not in methods:
                provide_automatic_options = True
                required_methods.add(‘OPTIONS‘)
            else:
                provide_automatic_options = False

        # Add the required methods now.
        methods |= required_methods

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(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(‘View function mapping is overwriting an ‘
                                     ‘existing endpoint function: %s‘ % endpoint)
            self.view_functions[endpoint] = view_func

可以看到,当在视图函数中没有指定endpoint时,程序会调用_endpoint_from_view_func方法为endpoint赋值

def _endpoint_from_view_func(view_func):
    assert view_func is not None, ‘expected view func if endpoint ‘                                   ‘is not provided.‘
    return view_func.__name__

可以看出,_endpoint_from_view_func实际上返回的就是view_func函数的函数名。

在上面的例子中,view_func指的是hello_world这个视图函数

所以此时,在options这个字典中有一个键为endpoint,对应的值为view_func函数的函数名

接着,程序从options字典中弹出"methods"的值,并对methods中的每个方法转换为大写,如果methods没有从程序中获取,则默认为"GET"

接着,程序从函数中获取"required_methods"的值,并进行去重,默认得到一个空集合

再对methods和required_methods进行"|="操作,也就是按位或运算

    |=(按位或)运算

    >>> a = 15
    >>> bin(a)
    ‘0b1111‘
    >>> b = 100
    >>> bin(b)
    ‘0b1100100‘
    >>> a |= b
    >>> a
    111
    >>> bin(a)
    ‘0b1101111‘
    >>> 0b1101111
    111

    先把a这个十进制数转换成二进制,得到1111
    再把b这个十进制数转换成二进制,得到1100100

    对a和b的二进制格式进行按位或运算

    a   000  0111
    b   110  0100
        110  0111

    因为a转换成二进制只有四位,如果要和b的二进制格式做位运算,则必须在头部填充0到和b的二进制相同的长度,得到"0000111"

    或运算中,只要有一个数为1,则这一位上做或运算的结果就为1
    所以上面两个数做或运算得到的二进制数为"0b1100111"
    把这个二进制数转换成十进制,则为111,把111这个十进制数赋值给a

对methods和required_methods进行按位或运算,实际上就是把required_methods的值添加到methods方法集合里

接着程序调用self.url_rule_class方法处理rule(也就是"/"),methods和options字典

得到rule这个对象,在这里self同样指的是app这个对象

可以看到,url_rule_class指向的是Rule这个类的内存地址

url_rule_class = Rule

然后用Map类实例化得到self.url_map对象,调用self.url_map对象中的add方法处理rule这个对象

self.url_map = Map()

分析了上面的app.route的流程,知道使用app对象的route方法装饰rule,实际上就是执行了add_url_rule这个方法

那如果定义一个视图函数,调用app对象中的add_url_rule方法来处理对应的rule,是不是也可以完成route的装饰器功能呢

    from flask import Flask
    app = Flask(__name__)

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

    def login():
        return "登录成功!!"

    app.add_url_rule("/login",endpoint=None,view_func=login,methods=["GET"])

启动这个项目,在浏览器中打开"http://127.0.0.1:5000/login"地址

得到的效果如下

由些我们可以知道,虽然flask的路由实现表面上是使用了route这个装饰器,实际上内部也是调用app对象中的add_url_rule方法来实现,类似于Django中中路由的用法

原文地址:https://www.cnblogs.com/renpingsheng/p/8747584.html

时间: 2024-11-05 14:57:26

源码解析flask的路由系统的相关文章

MVC 路由源码解析

//到页面底部下载源,配合效果跟好. public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { RouteConfig.RegisterRoutes(RouteTable.Routes); //调用RouteConfig类的RegisterRoutes方法注册路由 //RouteTable.Routes是一个储存Route的集合 } } 我们转到RouteConf

Laravel框架下路由的使用(源码解析)

本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文档看此类文章会让你在日常开发中更上一层楼. 废话不多说,我们开始本章的讲解. 入口 Laravel启动后,会先加载服务提供者.中间件等组件,在查找路由之前因为我们使用的是门面,所以先要查到Route的实体类. 注册 第一步当然还是通过服务提供者,因为这是laravel启动的关键,在 RouteSer

.Net Core 认证系统之Cookie认证源码解析

接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且不是前后端分离的架构(页面端具有服务端处理能力).移动端就不要考虑了,太麻烦.支持前后端分离前给移动端提供认证Api的一般采用JwtBearer认证,可以和IdentityServer4的password模式结合.很适用,但是id4的password模式各客户端必须绝对信任,因为要暴露用户名密码.适

.Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还需要掌握identity server4的基本用法,关于identity server4因为设计到两个协议Oath2.0和openid connect协议,内容较多,不是本文重点,后续有时间我会写一片关于identity server4的源码分析.且为了保证整个系统的高度可控,我重写了整个id4,留

Qt元对象系统源码解析

Qt元对象系统源码解析 https://blog.51cto.com/9291927/2070348 一.Qt元对象系统简介 1.元对象系统简介 Qt 的信号槽和属性系统基于在运行时进行内省的能力,所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力, 比如如果语言具有运行期间检查对象型别的能力,那么是型别内省(type intropection)的,型别内省可以用来实施多态.C++的内省比较有限,仅支持型别内省, C++的型别内省是通过运行时类型识别(RTTI)(Run-Time Typ

“Tornado源码解析篇”导读索引

最近花了2周时间断断续续地阅读了 Tornado 的源码,写了“Tornado源码解析”这个系列专题.由于写得比较散,这里简单做一个索引与导读. 为什么要选择 Tornado 这个框架?先给大家讲一个小故事:赌王娱乐城 "[web.py inspired the] web framework we use at FriendFeed [and] the webapp framework that ships with App Engine..." — Brett Taylor, co-

Flask最强攻略 - 跟DragonFire学Flask - 第七篇 Flask 中路由系统

Flask中的路由系统其实我们并不陌生了,从一开始到现在都一直在应用 @app.route("/",methods=["GET","POST"]) 为什么要这么用?其中的工作原理我们知道多少? 请关注跟DragonFire学Flask 之 路由系统 ,这里有你想要的答案 1. @app.route() 装饰器中的参数 如果不明白装饰器 点击这里 methods : 当前 url 地址,允许访问的请求方式 @app.route("/inf

Flask第六篇 Flask中路由系统

Flask中的路由系统其实我们并不陌生了,从一开始到现在都一直在应用 @app.route("/",methods=["GET","POST"]) 为什么要这么用?其中的工作原理我们知道多少? 请关注跟DragonFire学Flask 之 路由系统 ,这里有你想要的答案 1. @app.route() 装饰器中的参数 如果不明白装饰器 点击这里 methods : 当前 url 地址,允许访问的请求方式 @app.route("/inf

SPRING源码解析-SPRING 核心-IOC

IoC 和 AOP是Spring的核心, 是Spring系统中其他组件模块和应用开发的基础.透过这两个模块的设计和实现可以了解Spring倡导的对企业应用开发所应秉承的思路: 易用性. POJO开发企业应用, 直接依赖于Java语言,而不是容器和框架. 提升程序的可测试性,提高软件质量. 提供一致性编程模型,面向接口的编程 降低应用的负载和框架的侵入性.IoC和AOP实现. 不作为现有解决方案的替代,而是集成现有. IoC和AOP这两个核心组件,特别是IoC容器,使用户在使用Spring完成PO