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:添加路由

8481c8f592b7f349aa84a1de5c171db681516edfroute:路由装饰器

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中文社区”微信公众号
相关文章
|
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
|
16天前
|
Web App开发 数据采集 iOS开发
「Python+Dash快速web应用开发」
这是「Python+Dash快速web应用开发」系列教程的第十六期,本期将介绍如何在Dash应用中实现多页面路由功能。通过使用`Location()`组件监听URL变化,并结合回调函数动态渲染不同页面内容,使应用更加模块化和易用。教程包括基础用法、页面重定向及无缝跳转等技巧,并通过实例演示如何构建个人博客网站。
23 2
|
3天前
|
jenkins 测试技术 持续交付
自动化测试的高效之路:如何利用Python进行Web应用测试
【9月更文挑战第13天】在软件开发的快节奏中,自动化测试是确保质量和效率的关键。本文将引导你了解如何使用Python语言及其强大的测试框架来提升Web应用的测试效率。我们将一起探索编写简洁而强大的测试脚本的技巧,以及如何通过持续集成(CI)实现自动化测试流程。准备好让你的测试工作飞一般的感觉!
WK
|
13天前
|
数据采集 API 开发者
很少有人用python开发web???
Python 是一种流行且广泛使用的编程语言,尤其在 Web 开发领域。它凭借简洁的语法、丰富的框架(如 Django 和 Flask)、强大的库支持及活跃的社区,成为许多开发者和企业的首选。Python 的易学性、高效性及广泛的应用场景(包括 Web API、微服务和数据分析等)使其在 Web 开发中占据重要地位,并将持续发挥更大作用。
WK
35 0
|
17天前
|
数据库 Java 数据库连接
Struts 2 与 Hibernate 的完美邂逅:如何无缝集成两大框架,轻松玩转高效 CRUD 操作?
【8月更文挑战第31天】本文通过具体示例介绍了如何在 Struts 2 中整合 Hibernate,实现基本的 CRUD 操作。首先创建 Maven 项目并添加相关依赖,接着配置 Hibernate 并定义实体类及其映射文件。然后创建 DAO 接口及实现类处理数据库操作,再通过 Struts 2 的 Action 类处理用户请求。最后配置 `struts.xml` 文件并创建 JSP 页面展示用户列表及编辑表单。此示例展示了如何配置和使用这两个框架,使代码更加模块化和可维护。
25 0
|
17天前
|
Java 缓存 数据库连接
揭秘!Struts 2性能翻倍的秘诀:不可思议的优化技巧大公开
【8月更文挑战第31天】《Struts 2性能优化技巧》介绍了提升Struts 2 Web应用响应速度的关键策略,包括减少配置开销、优化Action处理、合理使用拦截器、精简标签库使用、改进数据访问方式、利用缓存机制以及浏览器与网络层面的优化。通过实施这些技巧,如懒加载配置、异步请求处理、高效数据库连接管理和启用GZIP压缩等,可显著提高应用性能,为用户提供更快的体验。性能优化需根据实际场景持续调整。
41 0