Python微型Web框架Bottle源码分析

简介: Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python。它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系。 选择源码分析的版本是 Release 于 2009 年 7 月 11 日的 0.4.10 (这是我能找到的最早的发布版本了)。

Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python。它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系。

选择源码分析的版本是 Release 于 2009 年 7 月 11 日的 0.4.10 (这是我能找到的最早的发布版本了)。

为什么要分析 Bottle 这个比较冷门的框架?

8481c8f592b7f349aa84a1de5c171db681516edfBottle 从发布至今一直贯彻的微型 Web 框架的理念。
8481c8f592b7f349aa84a1de5c171db681516edf Bottle 一直坚持单文件发布,也就是只有一个 bottle.py 文件。
8481c8f592b7f349aa84a1de5c171db681516edf 除了 Python 标准库之外没有依赖关系。
8481c8f592b7f349aa84a1de5c171db681516edf 与 Flask、Django 都遵循 PEP-3333 的 WSGI 协议。
8481c8f592b7f349aa84a1de5c171db681516edf 0.4.10 版本代码量小,加上大量注释也只有不到 1000 行的代码。

所以,抛开框架的高级功能,单单从一个 Web 框架怎么处理请求的角度来看,Bottle 是最佳的选择。

Flask 从第一版开始就是依赖于 werkzeug 实现,更多的实现细节需要从 werkzeug 中查找。

Django 是个重型框架,不适合整体代码阅读,各个组件看看就可以。

Tornado 是个异类,和 WSGI 没有什么关系。

在阅读之前最好从 Github 上下载一份 0.4.10 版本的 Bottle 的源码,边看边阅读本文。

阅读本文你需要有如下技能:

8481c8f592b7f349aa84a1de5c171db681516edf 熟悉 Python 的语法
8481c8f592b7f349aa84a1de5c171db681516edf 熟悉 HTTP 协议
8481c8f592b7f349aa84a1de5c171db681516edf 至少使用过一种 WSGI 的框架
8481c8f592b7f349aa84a1de5c171db681516edf 了解 CGI
8481c8f592b7f349aa84a1de5c171db681516edf 看得懂中文
流程结构分析

代码虽然不多,但是毫无目的的看难免思绪混乱,会看的心烦意乱,甚至会有产生「写的这是什么鬼?」的想法。

一个 Web 框架最核心也是最基本的功能就是处理 请求响应

但是在这之前,需要先创建一个 Server,才能开始处理啊!

所以大体的流程如下:

8481c8f592b7f349aa84a1de5c171db681516edf怎么创建一个 WSGI 的 Server 。
8481c8f592b7f349aa84a1de5c171db681516edf怎么处理到来的请求。
8481c8f592b7f349aa84a1de5c171db681516edf怎么处理响应。
创建 WSGI Server

在 Bottle 中关于创建一个标准的 WSGI Server 涉及的类或者方法只有 3 个。

注意,这里只关心一个标准的 WSGI,和核心功能。包括注释、错误处理、参数处理,会统统删除。

从文档中可以看到 Bottle 是通过一个 run 方法启动的。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

WSGIRefServer 继承自 ServerAdapter,并且覆盖了 run 方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这个 run 方法本身也是很简单,通过 Python 标准库中的 make_server 创建了一个 WSGI Server 然后跑了起来。

注意在 run 方法中的 WSGIHandler 和 WSGIRefServer.run 中的 handler 参数,这个就是如何处理一次请求和响应的关键所在。

在这之前,还需要先看看 Bottle 对 Request 和 Respouse 的定义。

Request 定义

Bottle 为每次请求都会把一些参数保存在当前的线程中,通过继承 threading.local 实现线程安全。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Request 是由一个方法和 8 个属性构成。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

bind 方法除了初始化一些变量以外,还添加 environ 到本次请求当中,environ 是一个字典包含了 CGI 的环境变量,更多 environ 内容参考PEP-3333 中 environ Variables 部分

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这三个属性比较简单,只是从 _environ 中取出了CGI 的某个环境变量。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

GET 属性把 query_string 解析成字典放入当前请求的变量中,所以在请求中获取 GET 方法的参数可以使用 requst.GET['xxxx'] 这样子的用法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

POST 属性从 wsgi.input 中获取内容(也就是表单提交的内容)放入当前请求的变量中,可以通过request.POST['xxxx'] 来获取数据。

从 GET 和 POST 这两属性的使用来看,包括 Flask 和 Django 都实现了类似的方法,这方法属性拥有一样的步骤就是获取数据,然后转换成标准的字典格式,实现上来看没什么复杂的,就是普通的字符串处理而已。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

params 属性提供了一个便利访问数据的方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Bottle 的 COOKIES 管理比较简单,只是单纯的从 CGI 中获取请求的 Cookie,如果存在的话直接返回。

以上就是 Bottle 的请求定义的内容。

简单总结来看,Request 从 CGI 中获取数据并且做一些数据处理,然后绑定到变量上。

Response 定义

整体结构和 Resquest 大致一样。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

bind 方法只是初始化了一些变量。其中比较有意思的是 HeaderDict。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

这是一个扩展于 dict 的字典,转化成大小写无关的 Title key ,还可以以列表方式添加多个成员。这个 HeaderDict 有意思的地方有两个:

8481c8f592b7f349aa84a1de5c171db681516edf 与大小无关的 Ttile key,也就是会吧 key 转成以大写头其他小写的 key
8481c8f592b7f349aa84a1de5c171db681516edf 存储重复 kv 值时候 values 会以 list 形式存储。如果 values 是多层 list,会自动解析成一层数据。
8481c8f592b7f349aa84a1de5c171db681516edf 重写 items 方法,以二元元组方式返回数据,包括多值数据。
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

Response 对 Cookie 的初始化,并且提供了设置的方法。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

为 content_type 属性提供了 set 和 get 方法,针对的是 Header 中的 Content-Type。

添加路由和 handler

这部分由一个装饰器和三个方法组成。

8481c8f592b7f349aa84a1de5c171db681516edf compile_route:路由正则
8481c8f592b7f349aa84a1de5c171db681516edf add_route:添加路由
8481c8f592b7f349aa84a1de5c171db681516edf route:路由装饰器
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

路由装饰器,简化 add_route 的调用。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

ROUTES_SIMPLE 和 ROUTES_REGEXP 是两个全局字典,用于存储路由相关数据(方法,参数,地址)。

简单路由放入 ROUTES_SIMPLE,以 method 为 key ,在 method 中再以路由地址为 key,处理函数 handler 为 value 存储。

复杂路由放入 ROUTES_REGEXP,以 method 为 key,以 route 和 handler 组成的元组列表存储。

处理请求和响应

根据 PEP-3333 文档需要为编写一个可调用对象(可以是函数,或者是具有 __call__ 方法的类)。

Bottle 中的 WSGIHandler 正是这么一个可调用对象。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=
640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

为了和代码契合度高,分析已经注释在当中。

处理流程如下:

8481c8f592b7f349aa84a1de5c171db681516edf拿到线程独立的 request 和 response
8481c8f592b7f349aa84a1de5c171db681516edfbind environ 数据
8481c8f592b7f349aa84a1de5c171db681516edf根据 match_url 找到处理的 handler 和参数,执行 
8481c8f592b7f349aa84a1de5c171db681516edf处理 Bottle 错误
8481c8f592b7f349aa84a1de5c171db681516edf处理内部错误
8481c8f592b7f349aa84a1de5c171db681516edf如果是文件则发送文件,不是的话正常返回字符串
8481c8f592b7f349aa84a1de5c171db681516edf设置 Set-Cookie header
8481c8f592b7f349aa84a1de5c171db681516edf结束
结束
Bottle 0.4.10 版本的核心内容就差么多,其他都是一些错误处理之类的。

该版本的 Bottle 以简单的过程,描述出了一个基于 WSGI 的 Web 框架是怎么样处理请求和响应的过程,完全基于 Python 标准库实现。


原文发布时间为:2017-05-22

本文作者:正小歪

本文来自云栖社区合作伙伴“Python中文社区”,了解相关信息可以关注“Python中文社区”微信公众号

相关文章
|
16天前
|
开发框架 JavaScript 前端开发
如何选择合适的Web开发框架?
【9月更文挑战第1天】如何选择合适的Web开发框架?
38 1
|
1天前
|
机器学习/深度学习 算法 开发工具
Python Web开发工具
Python Web开发工具
7 3
|
4天前
|
Python
使用Python和Flask构建简单的Web应用
使用Python和Flask构建简单的Web应用
18 6
|
4天前
|
SQL 安全 数据库
从入门到精通:Python Web安全守护指南,SQL注入、XSS、CSRF全防御!
【9月更文挑战第13天】在开发Python Web应用时,安全性至关重要。本文通过问答形式,详细介绍如何防范SQL注入、XSS及CSRF等常见威胁。通过使用参数化查询、HTML转义和CSRF令牌等技术,确保应用安全。附带示例代码,帮助读者从入门到精通Python Web安全。
22 6
|
5天前
|
SQL 安全 JavaScript
告别Web安全小白!Python实战指南:抵御SQL注入、XSS、CSRF的秘密武器!
【9月更文挑战第12天】在Web开发中,安全漏洞如同暗礁,尤其对初学者而言,SQL注入、跨站脚本(XSS)和跨站请求伪造(CSRF)是常见挑战。本文通过实战案例,展示如何利用Python应对这些威胁。首先,通过参数化查询防止SQL注入;其次,借助Jinja2模板引擎自动转义机制抵御XSS攻击;最后,使用Flask-WTF库生成和验证CSRF令牌,确保转账功能安全。掌握这些技巧,助你构建更安全的Web应用。
14 5
|
7天前
|
安全 Python
使用Python实现简单的Web服务器
使用Python实现简单的Web服务器
18 6
|
10天前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
50 3
|
12天前
|
JSON 测试技术 数据库
Python的Flask框架
Python的Flask框架
|
14天前
|
IDE 测试技术 持续交付
Python自动化测试与单元测试框架:提升代码质量与效率
【9月更文挑战第3天】随着软件行业的迅速发展,代码质量和开发效率变得至关重要。本文探讨了Python在自动化及单元测试中的应用,介绍了Selenium、Appium、pytest等自动化测试框架,以及Python标准库中的unittest单元测试框架。通过详细阐述各框架的特点与使用方法,本文旨在帮助开发者掌握编写高效测试用例的技巧,提升代码质量与开发效率。同时,文章还提出了制定测试计划、持续集成与测试等实践建议,助力项目成功。
38 5
|
15天前
|
机器学习/深度学习 PyTorch 算法框架/工具
python这些库和框架哪个更好
【9月更文挑战第2天】python这些库和框架哪个更好
30 6