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中文社区”微信公众号
相关文章
|
26天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL是关系型DB,依赖预定义的表格结构,适合结构化数据和复杂查询,但扩展性有限。NoSQL提供灵活的非结构化数据存储(如JSON),无统一查询语言,但能横向扩展,适用于大规模、高并发场景。选择取决于应用需求和扩展策略。
114 1
|
14天前
|
安全 数据库 C++
Python Web框架比较:Django vs Flask vs Pyramid
【4月更文挑战第9天】本文对比了Python三大Web框架Django、Flask和Pyramid。Django功能全面,适合快速开发,但学习曲线较陡;Flask轻量灵活,易于入门,但默认配置简单,需自行添加功能;Pyramid兼顾灵活性和可扩展性,适合不同规模项目,但社区及资源相对较少。选择框架应考虑项目需求和开发者偏好。
|
2天前
|
XML Web App开发 测试技术
python的Web自动化测试
【4月更文挑战第16天】Python在Web自动化测试中广泛应用,借助Selenium(支持多浏览器交互)、BeautifulSoup(解析HTML/XML)、Requests(发送HTTP请求)和Unittest(测试框架)等工具。测试步骤包括环境搭建、编写测试用例、初始化浏览器、访问页面、操作元素、验证结果、关闭浏览器及运行报告。注意浏览器兼容性、动态内容处理和错误处理。这些组合能提升测试效率和质量。
11 6
|
6天前
|
SQL 安全 Go
如何在 Python 中进行 Web 应用程序的安全性管理,例如防止 SQL 注入?
在Python Web开发中,确保应用安全至关重要,主要防范SQL注入、XSS和CSRF攻击。措施包括:使用参数化查询或ORM防止SQL注入;过滤与转义用户输入抵御XSS;添加CSRF令牌抵挡CSRF;启用HTTPS保障数据传输安全;实现强身份验证和授权系统;智能处理错误信息;定期更新及审计以修复漏洞;严格输入验证;并培训开发者提升安全意识。持续关注和改进是保证安全的关键。
13 0
|
8天前
|
数据库 开发者 Python
Python中使用Flask构建简单Web应用的例子
【4月更文挑战第15天】Flask是一个轻量级的Python Web框架,它允许开发者快速搭建Web应用,同时保持代码的简洁和清晰。下面,我们将通过一个简单的例子来展示如何在Python中使用Flask创建一个基本的Web应用。
|
8天前
|
前端开发 数据挖掘 API
使用Python中的Flask框架进行Web应用开发
【4月更文挑战第15天】在Python的Web开发领域,Flask是一个备受欢迎的轻量级Web框架。它简洁、灵活且易于扩展,使得开发者能够快速地构建出高质量的Web应用。本文将深入探讨Flask框架的核心特性、使用方法以及在实际开发中的应用。
|
21天前
|
数据采集 Java API
python并发编程: Python使用线程池在Web服务中实现加速
python并发编程: Python使用线程池在Web服务中实现加速
18 3
python并发编程: Python使用线程池在Web服务中实现加速
|
24天前
|
前端开发 JavaScript 数据管理
描述一个使用Python开发Web应用程序的实际项目经验,包括所使用的框架和技术栈。
使用Flask开发Web应用,结合SQLite、Flask-SQLAlchemy进行数据管理,HTML/CSS/JS(Bootstrap和jQuery)构建前端。通过Flask路由处理用户请求,模块化代码提高可维护性。unittest进行测试,开发阶段用内置服务器,生产环境可选WSGI服务器或容器化部署。实现了用户注册登录和数据管理功能,展示Python Web开发的灵活性和效率。
14 4
|
1月前
|
存储 前端开发 JavaScript
强烈推荐一个Python库!制作Web Gui也太简单了!
强烈推荐一个Python库!制作Web Gui也太简单了!
|
1月前
|
前端开发 API 网络架构
Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!
Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!