Flask源码实现(0.1版本)笔记

Posted 子睿闲谈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask源码实现(0.1版本)笔记相关的知识,希望对你有一定的参考价值。

使用Flask轻量级的web框架我们可以快速的开发出简单的api或者原型实现,知道Flask内部是怎么运作的吗?本文是基于0.1版本源码的笔记,对Flask有个初步的了解。0.1版本就是一个flask.py文件,主要实现了Flask类,代码及包括注释不到700行,非常精简。

Flask类

- 实现一个WSGI应用,扮演一个中心对象
- 通过传入应用的模块名或包名,一旦创建,将扮演有试图功能、URL规则、模版配置的注册中心

构造函数实现

    def __init__(self, package_name):
# 是否启动调试,调试模式下当代码检测到改变将自动重载
self.debug = False
self.package_name = package_name
self.root_path = _get_package_path(self.package_name)
# 保存注册的所有视图函数的字典,key是函数名,value是函数对象
self.view_functions = {}
# 保存所有注册的错误处理的字典,key是错误代码整数,value是错误处理函数
self.error_handlers = {}
# 请求在dispatch之前要调用的函数列表
self.before_request_funcs = []
# 请求结束后要调用的函数列表,输入response对象,修改它,或替换它
self.after_request_funcs = []
# 用于填充模板上下文的没参数调用函数列表,每个都返回一个字典
self.template_context_processors = [_default_template_ctx_processor]
# 直接使用werkzeug的路由映射
self.url_map = Map()

# 如果设置了静态路径,加入路由映射
if self.static_path is not None:
self.url_map.add(Rule(self.static_path + '/<filename>',
build_only=True, endpoint='static'))
if pkg_resources is not None:
target = (self.package_name, 'static')
else:
target = os.path.join(self.root_path, 'static')
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
self.static_path: target
})

# Jinja2环境
self.jinja_env = Environment(loader=self.create_jinja_loader(),
**self.jinja_options)
self.jinja_env.globals.update(
url_for=url_for,
get_flashed_messages=get_flashed_messages
)

装饰器app.route('/')实现

给定的URL规则注册视图函数,变量部分作为视图函数的关键字参数传入

  • 装饰器route()实现

    def route(self, rule, **options):
def decorator(f):
self.add_url_rule(rule, f.__name__, **options)
self.view_functions[f.__name__] = f
return f
return decorator
  • 处理URL尾部'/'的规则:

    • 如果规则没有以'/'结尾,请求带'/',将引发404错误

    • 如果规则是以'/'结尾,请求没有'/'的,将进行重定向带'/'的页面

  • 装饰器可接受的参数

    • rule:URL规则字符串

    • methods:默认是GET,方法列表

    • subdomain:子域?

    • strict_slashes:禁用斜线规则

    • options:其他可以被转发到底层(werkzeug.routing.Rule类)的选项

  • 关联URL规则(add_url_rule)

    • rule:URL规则字符串

    • endpoint:注册URL规则的endpoint,flask假设endpoint为视图函数名称

    • options:可以转发到底层类(werkzeug.routing.Rule)的选项

    • 支持的参数

    • 方法实现:

    def add_url_rule(self, rule, endpoint, **options):
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))

wsgi_app()方法实现

真实的WSGI应用,没有在__call__中实现,可以使用中间件

    def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied:

app.wsgi_app = MyMiddleware(app.wsgi_app)

:param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
"""

# 创建上下文
with self.request_context(environ):
# 预处理
rv = self.preprocess_request()
if rv is None:
# 调用URL对应的视图函数
rv = self.dispatch_request()
# 从视图的返回值转换为实际的响应对象
response = self.make_response(rv)
# 进行会话保存,以及调用所有after_request装饰器,返回response_class实例
response = self.process_response(response)
return response(environ, start_response)
  • 可接收的参数

    • environ:WSGI环境

    • start_response:状态码、headers列表和可选的异常上下文,回调响应

  • 视图函数返回值的类型

    • response_class:Response的类对象,直接返回,不改变

    • str:响应对象是字符串()

    • unicode:响应对象是使用utf-8编码的字符串

    • tuple:响应对象是元组

    • WSGI函数:调用WSGI应用,缓存响应对象

请求上下文类(_RequestContext)

请求上下文包含所有请求相关的信息。在请求开始时创建,添加到_request_ctx_stack,并最后移除。会创建适合WSGI环境的URL适配器和request对象。

class _RequestContext(object):
"""The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
"""


def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
# 创建或打开一个新的会话
self.session = app.open_session(self.request)
# g变量
self.g = _RequestGlobals()
self.flashes = None

def __enter__(self):
_request_ctx_stack.push(self)

def __exit__(self, exc_type, exc_value, tb):
# do not pop the request stack if we are in debug mode and an
# exception happened. This will allow the debugger to still
# access the request object in the interactive shell.
# 如果是调试模式或产生异常,将不移除请求,允许在交互式shell下继续访问request对象
if tb is None or not self.app.debug:
_request_ctx_stack.pop()


子睿闲谈 发起了一个读者讨论 欢迎留下您的精彩语录

如果文章对您有帮助,请友情帮转发+点赞哦!



以上是关于Flask源码实现(0.1版本)笔记的主要内容,如果未能解决你的问题,请参考以下文章

glusterfs4.0.1 mempool 分析笔记

flask route 设计思路

Flask 编写http接口api及接口自动化测试

Python学习笔记_05:使用Flask+MySQL实现用户登陆注册以及增删查改操作

python学习笔记-flask学习

Flask:操作SQLite3(0.1)