社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python微型Web框架Bottle源码分析

Python中文社区 • 6 年前 • 735 次点击  

專 欄


正小歪,Python 工程师,主要负责 Web 开发和日志数据处理。博客文章《真正的 Tornado 异步非阻塞》、《使用 JWT 让你的 RESTful API 更安全》等多次入选知名技术社区每日精选。《使用 Shipyard 搭建 Docker 集群》被选入 Dockerone 周报。
个人博客: https://www.hexiangyu.me
GitHub: https://github.com/zhengxiaowai

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

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

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

  • Bottle 从发布至今一直贯彻的微型 Web 框架的理念。

  • Bottle 一直坚持单文件发布,也就是只有一个 bottle.py 文件。

  • 除了 Python 标准库之外没有依赖关系。

  • 与 Flask、Django 都遵循 PEP-3333 的 WSGI 协议。

  • 0.4.10 版本代码量小,加上大量注释也只有不到 1000 行的代码。

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

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

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

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

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

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

  • 熟悉 Python 的语法

  • 熟悉 HTTP 协议

  • 至少使用过一种 WSGI 的框架

  • 了解 CGI

  • 看得懂中文

流程结构分析

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

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

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

所以大体的流程如下:

  1. 怎么创建一个 WSGI 的 Server 。

  2. 怎么处理到来的请求。

  3. 怎么处理响应。

创建 WSGI Server

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

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

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

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

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

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

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

Request 定义

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

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

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

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

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

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

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

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

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

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

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

Response 定义

整体结构和 Resquest 大致一样。

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

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

  • 与大小无关的 Ttile key,也就是会吧 key 转成以大写头其他小写的 key

  • 存储重复 kv 值时候 values 会以 list 形式存储。如果 values 是多层 list,会自动解析成一层数据。

  • 重写 items 方法,以二元元组方式返回数据,包括多值数据。

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

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

添加路由和 handler

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

  • compile_route:路由正则

  • add_route:添加路由

  • route:路由装饰器

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

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

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

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

处理请求和响应

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

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

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

处理流程如下:

  1. 拿到线程独立的 request 和 response

  2. bind environ 数据

  3. 根据 match_url 找到处理的 handler 和参数,执行 

    1. 处理 Bottle 错误

    2. 处理内部错误

  4. 如果是文件则发送文件,不是的话正常返回字符串

  5. 设置 Set-Cookie header

  6. 结束

结束

Bottle 0.4.10 版本的核心内容就差么多,其他都是一些错误处理之类的。

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

好哒,么么哒~~~,Python 大法好啊,Python 大法好啊,Python 大法好啊。


扫一扫关注Python中文社区,

获取更多技术干货!


Python 中 文 社 区

Python中文开发者的精神家园

合作、投稿请联系微信:

pythonpost

— 人生苦短,我用Python —





本文为作者原创作品,未经作者授权同意禁止转载

点击阅读原文可进入Python小密圈



原文
今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/weixin/u4TJ778PSe
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/2029
 
735 次点击