CherryPy - 快速指南
CherryPy - 简介
CherryPy 是 Python 的一个 Web 框架,为 Python 开发人员提供了与 HTTP 协议的友好接口。它也称为 Web 应用程序库。
CherryPy 使用 Python 作为动态语言的优势来建模 HTTP 协议并将其绑定到 API 中。它是最古老的 Python Web 框架之一,提供干净的界面和可靠的平台。
CherryPy 的历史
Remi Delon 于 2002 年 6 月下旬发布了 CherryPy 的第一个版本。这是一个成功的 Python Web 库的起点。Remi 是一位法国黑客,他相信 Python 是 Web 应用程序开发的最佳替代方案之一。
Remi 开发的项目吸引了许多对该方法感兴趣的开发人员。该方法包括以下特点 -
CherryPy 接近模型-视图-控制器模式。
CherryPy 类必须由 CherryPy 引擎处理和编译,以生成嵌入完整应用程序及其自己的内置 Web 服务器的独立 Python 模块。
CherryPy 可以将 URL 及其查询字符串映射到 Python 方法调用中,例如 -
http://somehost.net/echo?message=hello would map to echo(message='hello')
CherryPy项目的两年发展过程中,得到了社区的支持,Remi发布了多个改进版本。
2004 年 6 月,人们开始讨论该项目的未来以及是否应该继续使用相同的架构。经过几位项目常客的集思广益和讨论,提出了对象发布引擎和过滤器的概念,它很快成为 CherryPy2 的核心部分。后来,在 2004 年 10 月,发布了 CherryPy 2 alpha 的第一个版本,作为概念证明这些核心思想。CherryPy 2.0 取得了真正的成功;然而,人们认识到其设计仍然可以改进,并且需要重构。
经过基于反馈的讨论,CherryPy 的 API 进一步修改以提高其优雅性,最终于 2005 年 10 月发布了 CherryPy 2.1.0。经过各种更改,团队于 2006 年 4 月发布了 CherryPy 2.2.0。
CherryPy 的优势
CherryPy 的以下功能被认为是其优势 -
简单
在 CherryPy 中开发项目是一项简单的任务,只需根据 Python 的约定和缩进开发几行代码。
CherryPy 也非常模块化。主要组件通过正确的逻辑概念得到良好的管理,并且父类可以扩展到子类。
力量
CherryPy 利用了 Python 的所有功能。它还提供工具和插件,它们是开发世界级应用程序所需的强大扩展点。
开源
CherryPy是一个开源Python Web框架(在开源BSD许可下授权),这意味着该框架可以零成本商业使用。
社区帮助
它有一个专门的社区,提供各种类型的问题和答案的完整支持。社区试图为开发者提供从初级到高级的全面帮助。
部署
有多种经济有效的方法来部署应用程序。CherryPy 包含自己的生产就绪型 HTTP 服务器来托管您的应用程序。CherryPy 还可以部署在任何符合 WSGI 的网关上。
CherryPy - 环境设置
CherryPy 与大多数开源项目一样都有软件包,可以通过多种方式下载和安装,如下所示 -
- 使用压缩包
- 使用easy_install
- 使用 Subversion
要求
安装 CherryPy 框架的基本要求包括 -
- Python 2.4 或以上版本
- 樱桃Py 3.0
安装 Python 模块被认为是一个简单的过程。安装包括使用以下命令。
python setup.py build python setup.py install
Python 的包存储在以下默认目录中 -
- 在 UNIX 或 Linux 上,
/usr/local/lib/python2.4/site-packages or /usr/lib/python2.4/site-packages
- 在微软 Windows 上,
C:\Python or C:\Python2x
- 在 Mac 操作系统上,
Python:Lib:site-package
使用 Tarball 安装
Tarball 是文件或目录的压缩存档。CherryPy 框架为其每个版本(alpha、beta 和稳定版)提供了一个 Tarball。
它包含该库的完整源代码。该名称来自 UNIX 和其他操作系统中使用的实用程序。
以下是使用 tar 球安装 CherryPy 的步骤 -
步骤 1 - 根据用户要求从 http://download.cherrypy.org/下载版本
步骤 2 - 搜索下载 Tarball 的目录并将其解压缩。对于 Linux 操作系统,输入以下命令 -
tar zxvf cherrypy-x.y.z.tgz
对于 Microsoft Windows,用户可以使用 7-Zip 或 Winzip 等实用程序通过图形界面解压缩存档。
步骤 3 - 移动到新创建的目录并使用以下命令构建 CherryPy -
python setup.py build
对于全局安装,应使用以下命令 -
python setup.py install
使用 easy_install 安装
Python Enterprise Application Kit (PEAK) 提供了一个名为 Easy Install 的 python 模块。这有利于 Python 包的部署。该模块简化了下载、构建和部署Python应用程序和产品的过程。
在安装 CherryPy 之前,需要在系统中安装 Easy Install。
步骤 1 - 从http://peak.telecommunity.com下载 ez_setup.py 模块,并使用计算机上的管理权限运行它:python ez_setup.py。
步骤 2 - 以下命令用于安装 Easy Install。
easy_install product_name
步骤 3 - easy_install 将搜索 Python 包索引 (PyPI) 以查找给定的产品。PyPI 是所有 Python 产品的集中信息存储库。
使用以下命令部署 CherryPy 的最新可用版本 -
easy_install cherrypy
步骤 4 - easy_install 将下载 CherryPy、构建并将其全局安装到您的 Python 环境中。
使用 Subversion 安装
在以下情况下建议使用 Subversion 安装 CherryPy -
存在某个功能或已修复错误,并且仅在正在开发的代码中可用。
当开发人员在 CherryPy 本身上工作时。
当用户需要版本控制存储库中主分支的分支时。
用于修复先前版本的错误。
颠覆的基本原理是注册一个存储库并跟踪每个版本,其中包括一系列更改。
请按照以下步骤了解使用 Subversion 安装 CherryPy:
步骤 1 - 要使用项目的最新版本,有必要检查 Subversion 存储库中的 trunk 文件夹。
步骤 2 - 从 shell 输入以下命令 -
svn co http://svn.cherrypy.org/trunk cherrypy
步骤 3 - 现在,创建一个 CherryPy 目录并将完整的源代码下载到其中。
测试安装
需要验证应用程序是否已正确安装在系统中,就像我们对Java等应用程序所做的那样。
您可以选择上一章提到的三种方法中的任何一种在您的环境中安装和部署 CherryPy。CherryPy 必须能够从 Python shell 导入,如下所示 -
import cherrypy cherrypy.__version__ '3.0.0'
如果 CherryPy 没有全局安装到本地系统的 Python 环境,那么您需要设置 PYTHONPATH 环境变量,否则它将按以下方式显示错误 -
import cherrypy Traceback (most recent call last): File "<stdin>", line 1, in ? ImportError: No module named cherrypy
CherryPy - 词汇
为了理解 CherryPy 的工作原理,需要定义一些重要的关键字。关键字和定义如下 -
序列号 | 关键字及定义 |
---|---|
1. | 网络服务器 它是处理HTTP协议的接口。其目标是将 HTTP 请求转换为应用程序服务器,以便它们获得响应。 |
2. | 应用 它是一个收集信息的软件。 |
3. | 应用服务器 它是保存一个或多个应用程序的组件 |
4. | 网络应用服务器 它是Web服务器和应用服务器的组合。 |
例子
以下示例显示了 CherryPy 的示例代码 -
import cherrypy class demoExample: def index(self): return "Hello World!!!" index.exposed = True cherrypy.quickstart(demoExample())
现在让我们了解代码是如何工作的 -
名为CherryPy的包始终在指定的类中导入,以确保正常运行。
在上面的例子中,名为index的函数返回参数“Hello World!!!”。
最后一行启动 Web 服务器并调用指定的类(此处为 demoExample)并返回默认函数索引中提到的值。
示例代码返回以下输出 -
内置 Http 服务器和内部引擎
CherryPy 带有自己的 Web (HTTP) 服务器。这就是为什么 CherryPy 是独立的,并允许用户在获取库后几分钟内运行 CherryPy 应用程序。
Web服务器充当应用程序的网关,所有请求和响应都在其帮助下保持跟踪。
要启动 Web 服务器,用户必须进行以下调用 -
cherryPy.server.quickstart()
CherryPy 的内部引擎负责以下活动 -
- 创建和管理请求和响应对象。
- 控制和管理 CherryPy 进程。
CherryPy – 配置
该框架带有自己的配置系统,允许您参数化 HTTP 服务器。配置的设置可以存储在语法接近 INI 格式的文本文件中,也可以存储为完整的 Python 字典。
要配置 CherryPy 服务器实例,开发人员需要使用设置的全局部分。
global_conf = { 'global': { 'server.socket_host': 'localhost', 'server.socket_port': 8080, }, } application_conf = { '/style.css': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(_curdir, 'style.css'), } } This could be represented in a file like this: [global] server.socket_host = "localhost" server.socket_port = 8080 [/style.css] tools.staticfile.on = True tools.staticfile.filename = "/full/path/to.style.css"
HTTP 合规性
CherryPy发展缓慢,但它包括对HTTP规范的编译,支持HTTP/1.0,后来又支持HTTP/1.1。
CherryPy 据说有条件地符合 HTTP/1.1,因为它实现了规范的所有必须和必需级别,但不是所有应该级别。因此,CherryPy 支持 HTTP/1.1 的以下功能 -
如果客户端声称支持 HTTP/1.1,则它必须在使用指定协议版本发出的任何请求中发送标头字段。如果没有完成,CherryPy 将立即停止请求的处理。
CherryPy 生成一个在所有配置中使用的日期标头字段。
CherryPy 可以在客户端的支持下处理响应状态代码 (100)。
CherryPy 的内置 HTTP 服务器通过使用 Connection: Keep-Alive 标头支持 HTTP/1.1 中默认的持久连接。
CherryPy 正确处理分块请求和响应。
CherryPy 以两种不同的方式支持请求 - If-Modified-Since 和 If-Unmodified-Since 标头,并根据请求相应地发送响应。
CherryPy 允许任何 HTTP 方法。
CherryPy 处理客户端和服务器设置集之间的 HTTP 版本组合。
多线程应用服务器
CherryPy 是基于多线程概念设计的。每次开发人员在 CherryPy 命名空间中获取或设置一个值时,都是在多线程环境中完成的。
cherrypy.request 和cherrypy.response 都是线程数据容器,这意味着您的应用程序通过知道在运行时通过它们代理哪个请求来独立调用它们。
使用线程模式的应用程序服务器并未受到高度重视,因为线程的使用被认为会增加由于同步要求而出现问题的可能性。
其他选择包括 -
多进程模式
每个请求都由其自己的 Python 进程处理。这里可以认为服务器的性能和稳定性比较好。
异步模式
在这里,接受新连接并将数据发送回客户端是与请求过程异步完成的。该技术以其高效而闻名。
URL调度
CherryPy 社区希望更加灵活,并且希望为调度员提供其他解决方案。CherryPy 3 提供了其他内置调度程序,并提供了一种编写和使用您自己的调度程序的简单方法。
- 用于开发 HTTP 方法的应用程序。(获取、发布、放置等)
- 在 URL 中定义路由的路由调度程序
HTTP 方法调度程序
在某些应用程序中,URI 独立于服务器对资源执行的操作。
例如,http://xyz.com/album/delete/10
URI 包含客户端希望执行的操作。
默认情况下,CherryPy 调度程序将按以下方式映射 -
album.delete(12)
上面提到的调度程序是正确提到的,但可以通过以下方式使其独立 -
http://xyz.com/album/10
用户可能想知道服务器如何调度确切的页面。该信息由 HTTP 请求本身携带。当客户端向服务器发出请求时,CherryPy 会寻找最合适的处理程序,该处理程序是 URI 所针对的资源的表示。
DELETE /album/12 HTTP/1.1
路线调度员
以下是调度所需方法的参数列表 -
name 参数是要连接的路由的唯一名称。
路由是匹配 URI 的模式。
控制器是包含页面处理程序的实例。
使用路由调度程序连接匹配 URI 的模式并关联特定的页面处理程序。
例子
让我们举个例子来理解它是如何工作的 -
import random import string import cherrypy class StringMaker(object): @cherrypy.expose def index(self): return "Hello! How are you?" @cherrypy.expose def generate(self, length=9): return ''.join(random.sample(string.hexdigits, int(length))) if __name__ == '__main__': cherrypy.quickstart(StringMaker ())
按照下面给出的步骤获取上述代码的输出 -
步骤 1 - 将上述文件保存为tutRoutes.py。
步骤 2 - 访问以下 URL -
http://localhost:8080/generate?length=10
步骤 3 - 您将收到以下输出 -
CherryPy - 工具箱
在 CherryPy 中,内置工具提供了一个调用 CherryPy 库的接口。CherryPy 中定义的工具可以通过以下方式实现 -
- 从配置设置
- 作为 Python 装饰器或通过页面处理程序的特殊 _cp_config 属性
- 作为可从任何函数内应用的 Python 可调用对象
基本认证工具
该工具的目的是为应用程序中设计的应用程序提供基本的身份验证。
论点
该工具使用以下参数 -
姓名 | 默认 | 描述 |
---|---|---|
领域 | 不适用 | 定义领域值的字符串。 |
用户 | 不适用 | 形式的字典 - 用户名:密码或返回此类字典的 Python 可调用函数。 |
加密 | 没有任何 | Python callable 用于对客户端返回的密码进行加密,并将其与用户字典中提供的加密密码进行比较。 |
例子
让我们举个例子来理解它是如何工作的 -
import sha import cherrypy class Root: @cherrypy.expose def index(self): return """ <html> <head></head> <body> <a href = "admin">Admin </a> </body> </html> """ class Admin: @cherrypy.expose def index(self): return "This is a private area" if __name__ == '__main__': def get_users(): # 'test': 'test' return {'test': 'b110ba61c4c0873d3101e10871082fbbfd3'} def encrypt_pwd(token): return sha.new(token).hexdigest() conf = {'/admin': {'tools.basic_auth.on': True, tools.basic_auth.realm': 'Website name', 'tools.basic_auth.users': get_users, 'tools.basic_auth.encrypt': encrypt_pwd}} root = Root() root.admin = Admin() cherrypy.quickstart(root, '/', config=conf)
get_users函数返回一个硬编码字典,但也从数据库或其他任何地方获取值。类 admin 包含此功能,它利用 CherryPy 的内置身份验证工具。身份验证对密码和用户 ID 进行加密。
基本的身份验证工具并不真正安全,因为入侵者可以对密码进行编码和解码。
缓存工具
该工具的目的是为 CherryPy 生成的内容提供内存缓存。
论点
该工具使用以下参数 -
姓名 | 默认 | 描述 |
---|---|---|
无效方法 | (“发布”、“放置”、“删除”) | 不缓存的 HTTP 方法字符串元组。这些方法还会使资源的任何缓存副本失效(删除)。 |
缓存类 | 内存缓存 | 用于缓存的类对象 |
解码工具
该工具的目的是解码传入的请求参数。
论点
该工具使用以下参数 -
姓名 | 默认 | 描述 |
---|---|---|
编码 | 没有任何 | 它查找内容类型标头 |
默认编码 | “UTF-8” | 未提供或未找到时使用的默认编码。 |
例子
让我们举个例子来理解它是如何工作的 -
import cherrypy from cherrypy import tools class Root: @cherrypy.expose def index(self): return """ <html> <head></head> <body> <form action = "hello.html" method = "post"> <input type = "text" name = "name" value = "" /> <input type = ”submit” name = "submit"/> </form> </body> </html> """ @cherrypy.expose @tools.decode(encoding='ISO-88510-1') def hello(self, name): return "Hello %s" % (name, ) if __name__ == '__main__': cherrypy.quickstart(Root(), '/')
上面的代码从用户处获取一个字符串,并将用户重定向到“hello.html”页面,该页面将显示为带有给定名称的“Hello”。
上述代码的输出如下 -
hello.html
CherryPy - 一个有效的应用程序
全栈应用程序提供了通过某些命令或文件执行来创建新应用程序的工具。
考虑像 web2py 框架这样的 Python 应用程序;整个项目/应用程序是根据 MVC 框架创建的。同样,CherryPy 允许用户根据自己的要求设置和配置代码布局。
在本章中,我们将详细学习如何创建 CherryPy 应用程序并执行它。
文件系统
应用程序的文件系统如下图所示 -
以下是文件系统中各种文件的简要描述 -
config.py - 每个应用程序都需要一个配置文件和加载它的方法。该功能可以在 config.py 中定义。
controllers.py - MVC 是用户遵循的流行设计模式。controllers.py 是实现所有将安装在cherrypy.tree上的对象的地方。
models.py - 该文件直接与数据库交互以提供某些服务或存储持久数据。
server.py - 该文件与生产就绪的 Web 服务器交互,该服务器与负载平衡代理一起正常工作。
静态- 它包括所有 CSS 和图像文件。
视图- 它包括给定应用程序的所有模板文件。
例子
让我们详细了解创建 CherryPy 应用程序的步骤。
步骤 1 - 创建一个应包含该应用程序的应用程序。
步骤 2 - 在目录中,创建与项目对应的 python 包。创建 gedit 目录并在其中包含 _init_.py 文件。
步骤 3 - 在包内,包含包含以下内容的controllers.py 文件 -
#!/usr/bin/env python import cherrypy class Root(object): def __init__(self, data): self.data = data @cherrypy.expose def index(self): return 'Hi! Welcome to your application' def main(filename): data = {} # will be replaced with proper functionality later # configuration file cherrypy.config.update({ 'tools.encode.on': True, 'tools.encode.encoding': 'utf-8', 'tools.decode.on': True, 'tools.trailing_slash.on': True, 'tools.staticdir.root': os.path.abspath(os.path.dirname(__file__)), }) cherrypy.quickstart(Root(data), '/', { '/media': { 'tools.staticdir.on': True, 'tools.staticdir.dir': 'static' } }) if __name__ == '__main__': main(sys.argv[1])
步骤 4 - 考虑一个用户通过表单输入值的应用程序。让我们在应用程序中包含两个表单:index.html 和submit.html。
步骤 5 - 在上面的控制器代码中,我们有index(),它是一个默认函数,如果调用特定控制器,它会首先加载。
步骤 6 - index()方法的实现可以通过以下方式更改 -
@cherrypy.expose def index(self): tmpl = loader.load('index.html') return tmpl.generate(title='Sample').render('html', doctype='html')
步骤 7 - 这将在启动给定应用程序时加载 index.html 并将其定向到给定输出流。index.html 文件如下 -
索引.html
<!DOCTYPE html > <html> <head> <title>Sample</title> </head> <body class = "index"> <div id = "header"> <h1>Sample Application</h1> </div> <p>Welcome!</p> <div id = "footer"> <hr> </div> </body> </html>
步骤 8 -如果您想创建一个接受名称和标题等值的表单,那么向controller.py中的Root 类添加一个方法非常重要。
@cherrypy.expose def submit(self, cancel = False, **value): if cherrypy.request.method == 'POST': if cancel: raise cherrypy.HTTPRedirect('/') # to cancel the action link = Link(**value) self.data[link.id] = link raise cherrypy.HTTPRedirect('/') tmp = loader.load('submit.html') streamValue = tmp.generate() return streamValue.render('html', doctype='html')
步骤 9 - Submit.html 中包含的代码如下 -
<!DOCTYPE html> <head> <title>Input the new link</title> </head> <body class = "submit"> <div id = " header"> <h1>Submit new link</h1> </div> <form action = "" method = "post"> <table summary = ""> <tr> <th><label for = " username">Your name:</label></th> <td><input type = " text" id = " username" name = " username" /></td> </tr> <tr> <th><label for = " url">Link URL:</label></th> <td><input type = " text" id=" url" name= " url" /></td> </tr> <tr> <th><label for = " title">Title:</label></th> <td><input type = " text" name = " title" /></td> </tr> <tr> <td></td> <td> <input type = " submit" value = " Submit" /> <input type = " submit" name = " cancel" value = "Cancel" /> </td> </tr> </table> </form> <div id = "footer"> </div> </body> </html>
步骤 10 - 您将收到以下输出 -
这里,方法名称被定义为“POST”。交叉验证文件中指定的方法始终很重要。如果该方法包括“POST”方法,则应在数据库中的适当字段中重新检查这些值。
如果方法包含“GET”方法,则要保存的值将在 URL 中可见。
CherryPy - 网络服务
Web 服务是一组基于 Web 的组件,有助于在应用程序或系统之间交换数据,其中还包括开放协议和标准。它可以在网络上发布、使用和查找。
Web 服务有多种类型,例如 RWS(RESTfUL Web 服务)、WSDL、SOAP 等等。
REST — 代表性状态转移
一种远程访问协议,它将状态从客户端传输到服务器,可用于操纵状态而不是调用远程过程。
不定义任何特定的编码或结构以及返回有用错误消息的方式。
使用 HTTP“动词”来执行状态传输操作。
资源使用 URL 进行唯一标识。
它不是 API,而是 API 传输层。
REST 维护网络上资源的命名法,并提供统一的机制来对这些资源执行操作。每个资源由至少一个标识符来标识。如果 REST 基础设施是基于 HTTP 实现的,那么这些标识符被称为统一资源标识符 (URI)。
以下是 URI 集的两个常见子集 -
子集 | 完整形式 | 例子 |
---|---|---|
网址 | 统一资源定位器 | http://www.gmail.com/ |
瓮 | 统一资源名称 | 瓮:isbn:0-201-71088-9 瓮:uuid:13e8cf26-2a25-11db-8693-000ae4ea7d46 |
在了解CherryPy架构的实现之前,我们先来重点了解一下CherryPy的架构。
CherryPy 包括以下三个组件 -
cherrypy.engine - 它控制进程启动/拆卸和事件处理。
cherrypy.server - 它配置和控制 WSGI 或 HTTP 服务器。
cherrypy.tools - 与处理 HTTP 请求正交的实用程序工具箱。
通过 CherryPy 的 REST 接口
RESTful Web 服务在以下内容的帮助下实现了 CherryPy 架构的每个部分 -
- 验证
- 授权
- 结构
- 封装
- 错误处理
验证
身份验证有助于验证与我们交互的用户。CherryPy 包含处理每种身份验证方法的工具。
def authenticate(): if not hasattr(cherrypy.request, 'user') or cherrypy.request.user is None: # < Do stuff to look up your users > cherrypy.request.authorized = False # This only authenticates. Authz must be handled separately. cherrypy.request.unauthorized_reasons = [] cherrypy.request.authorization_queries = [] cherrypy.tools.authenticate = \ cherrypy.Tool('before_handler', authenticate, priority=10)
上述函数authenticate()将有助于验证客户端或用户的存在。内置工具有助于以系统的方式完成该过程。
授权
授权有助于通过 URI 维护流程的健全性。该过程还有助于通过用户令牌引导来变形对象。
def authorize_all(): cherrypy.request.authorized = 'authorize_all' cherrypy.tools.authorize_all = cherrypy.Tool('before_handler', authorize_all, priority=11) def is_authorized(): if not cherrypy.request.authorized: raise cherrypy.HTTPError("403 Forbidden", ','.join(cherrypy.request.unauthorized_reasons)) cherrypy.tools.is_authorized = cherrypy.Tool('before_handler', is_authorized, priority = 49) cherrypy.config.update({ 'tools.is_authorized.on': True, 'tools.authorize_all.on': True })
正如前面的示例中提到的,内置的授权工具有助于以系统的方式处理例程。
结构
维护 API 的结构有助于减少映射应用程序 URI 的工作量。保持 API 的可发现性和清洁性始终是必要的。CherryPy 框架的 API 基本结构应具有以下内容 -
- 帐户和用户
- 自动回复器
- 接触
- 文件
- 文件夹
- 列表和字段
- 消息和批量
封装
封装有助于创建轻量级、人类可读且可供各种客户端访问的 API。项目列表以及创建、检索、更新和删除需要封装 API。
错误处理
如果 API 无法按特定本能执行,则此过程会管理错误(如果有)。例如,400 表示错误请求,403 表示未经授权的请求。
例子
请考虑以下作为数据库、验证或应用程序错误的示例。
import cherrypy import json def error_page_default(status, message, traceback, version): ret = { 'status': status, 'version': version, 'message': [message], 'traceback': traceback } return json.dumps(ret) class Root: _cp_config = {'error_page.default': error_page_default} @cherrypy.expose def index(self): raise cherrypy.HTTPError(500, "Internal Sever Error") cherrypy.quickstart(Root())
上面的代码将产生以下输出 -
由于内置的访问工具,通过 CherryPy 可以轻松管理 API(应用程序编程接口)。
HTTP 方法
对资源进行操作的 HTTP 方法列表如下:
序列号 | HTTP方法及操作 |
---|---|
1. | 头 检索资源元数据。 |
2. | 得到 检索资源元数据和内容。 |
3. | 邮政 请求服务器使用请求正文中包含的数据创建新资源。 |
4. | 放 请求服务器将现有资源替换为请求正文中包含的资源。 |
5. | 删除 请求服务器删除该 URI 标识的资源。 |
6. | 选项 请求服务器返回有关全局或特定资源的功能的详细信息。 |
Atom 发布协议 (APP)
APP 源于 Atom 社区,作为 HTTP 之上的应用程序级协议,允许发布和编辑 Web 资源。APP 服务器和客户端之间的消息单元基于 Atom XML 文档格式。
Atom 发布协议使用 HTTP 及其机制以及 Atom XML 文档格式作为消息单元,定义了 APP 服务和用户代理之间的一组操作。
APP首先定义一个服务文档,该文档向用户代理提供APP服务所服务的不同集合的URI。
例子
让我们举个例子来演示 APP 是如何工作的 -
<?xml version = "1.0" encoding = "UTF-8"?> <service xmlns = "http://purl.org/atom/app#" xmlns:atom = "http://www.w3.org/2005/Atom"> <workspace> <collection href = "http://host/service/atompub/album/"> <atom:title> Albums</atom:title> <categories fixed = "yes"> <atom:category term = "friends" /> </categories> </collection> <collection href = "http://host/service/atompub/film/"> <atom:title>Films</atom:title> <accept>image/png,image/jpeg</accept> </collection> </workspace> </service>
APP 指定如何使用 HTTP 方法对集合成员或集合本身执行基本 CRUD 操作,如下表所示 -
手术 | HTTP方法 | 状态码 | 内容 |
---|---|---|---|
取回 | 得到 | 200 | 代表资源的 Atom 条目 |
创造 | 邮政 | 201 | 通过 Location 和 Content-Location 标头新创建的资源的 URI |
更新 | 放 | 200 | 代表资源的 Atom 条目 |
删除 | 删除 | 200 | 没有任何 |
CherryPy - 表示层
表示层确保通过它的通信以预期的接收者为目标。CherryPy 通过各种模板引擎维护表示层的工作。
模板引擎在业务逻辑的帮助下获取页面的输入,然后将其处理到仅针对目标受众的最终页面。
Kid——模板引擎
Kid是一个简单的模板引擎,其中包括要处理的模板的名称(这是强制性的)和渲染模板时要传递的数据的输入。
第一次创建模板时,Kid 创建一个 Python 模块,该模块可以用作模板的缓存版本。
Kid.Template函数返回模板类的实例,可用于呈现输出内容。
模板类提供以下命令集 -
序列号 | 命令与说明 |
---|---|
1. | 连载 它将输出内容作为字符串返回。 |
2. | 产生 它将输出内容作为迭代器返回。 |
3. | 写 它将输出内容转储到文件对象中。 |
这些命令使用的参数如下 -
序列号 | 命令与说明 |
---|---|
1. | 编码 它告知如何对输出内容进行编码 |
2. | 分段 它是一个布尔值,告诉 XML prolog 或 Doctype |
3. | 输出 这种类型的序列化用于呈现内容 |
例子
让我们举个例子来了解孩子是如何工作的 -
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html xmlns:py = "http://purl.org/kid/ns#"> <head> <title>${title}</title> <link rel = "stylesheet" href = "style.css" /> </head> <body> <p>${message}</p> </body> </html> The next step after saving the file is to process the template via the Kid engine. import kid params = {'title': 'Hello world!!', 'message': 'CherryPy.'} t = kid.Template('helloworld.kid', **params) print t.serialize(output='html')
孩子的属性
以下是 Kid 的属性 -
基于 XML 的模板语言
它是一种基于 XML 的语言。Kid 模板必须是具有正确命名约定的格式良好的 XML 文档。
Kid 在 XML 元素中实现属性,以更新底层引擎到达该元素时要遵循的操作。为了避免与 XML 文档中的其他现有属性重叠,Kid 引入了自己的命名空间。
<p py:if = "...">...</p>
变量替换
Kid 附带了一个变量替换方案和一个简单的方法 - ${variable-name}。
变量可以用在元素的属性中,也可以用作元素的文本内容。Kid 每次执行时都会评估该变量。
如果用户需要将文字字符串输出为 ${something},则可以使用变量替换(通过加倍美元符号)对其进行转义。
条件语句
为了在模板中切换不同的情况,使用以下语法 -
<tag py:if = "expression">...</tag>
这里,tag 是元素的名称,例如 DIV 或 SPAN。
该表达式是Python 表达式。如果作为布尔值其计算结果为 True,则该元素将包含在输出内容中,否则它将不会成为输出内容的一部分。
循环机制
为了循环 Kid 中的元素,使用以下语法 -
<tag py:for = "expression">...</tag>
这里,tag是元素的名称。该表达式是一个 Python 表达式,例如 [...] 中的值。
例子
以下代码显示了循环机制的工作原理 -
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>${title}</title> <link rel = "stylesheet" href = "style.css" /> </head> <body> <table> <caption>A few songs</caption> <tr> <th>Artist</th> <th>Album</th> <th>Title</th> </tr> <tr py:for = "info in infos"> <td>${info['artist']}</td> <td>${info['album']}</td> <td>${info['song']}</td> </tr> </table> </body> </html> import kid params = discography.retrieve_songs() t = kid.Template('songs.kid', **params) print t.serialize(output='html')
上述具有循环机制的代码的输出如下 -
CherryPy - Ajax 的使用
到 2005 年为止,所有 Web 应用程序遵循的模式是每个页面管理一个 HTTP 请求。从一个页面导航到另一页面需要加载完整页面。这会在更大程度上降低性能。
因此,用于嵌入 AJAX、XML 和 JSON 的富客户端应用程序有所增加。
AJAX
异步 JavaScript 和 XML (AJAX) 是一种创建快速动态网页的技术。AJAX 允许通过在后台与服务器交换少量数据来异步更新网页。这意味着可以更新网页的部分内容,而无需重新加载整个页面。
Google 地图、Gmail、YouTube 和 Facebook 是 AJAX 应用程序的几个示例。
Ajax 基于使用 JavaScript 发送 HTTP 请求的思想;更具体地说,AJAX 依赖 XMLHttpRequest 对象及其 API 来执行这些操作。
JSON
JSON 是一种携带序列化 JavaScript 对象的方法,JavaScript 应用程序可以评估它们并将它们转换为稍后可以操作的 JavaScript 对象。
例如,当用户向服务器请求 JSON 格式的相册对象时,服务器将返回如下输出 -
{'description': 'This is a simple demo album for you to test', 'author': ‘xyz’}
现在数据是一个 JavaScript 关联数组,并且可以通过以下方式访问描述字段:
data ['description'];
将 AJAX 应用于应用程序
考虑该应用程序,其中包含一个名为“media”的文件夹,其中包含index.html和Jquery插件,以及一个具有AJAX实现的文件。让我们将文件的名称视为“ajax_app.py”
ajax_app.py
import cherrypy import webbrowser import os import simplejson import sys MEDIA_DIR = os.path.join(os.path.abspath("."), u"media") class AjaxApp(object): @cherrypy.expose def index(self): return open(os.path.join(MEDIA_DIR, u'index.html')) @cherrypy.expose def submit(self, name): cherrypy.response.headers['Content-Type'] = 'application/json' return simplejson.dumps(dict(title="Hello, %s" % name)) config = {'/media': {'tools.staticdir.on': True, 'tools.staticdir.dir': MEDIA_DIR,} } def open_page(): webbrowser.open("http://127.0.0.1:8080/") cherrypy.engine.subscribe('start', open_page) cherrypy.tree.mount(AjaxApp(), '/', config=config) cherrypy.engine.start()
“AjaxApp”类重定向到“index.html”网页,该网页包含在媒体文件夹中。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns = "http://www.w3.org/1999/xhtml" lang = "en" xml:lang = "en"> <head> <title>AJAX with jQuery and cherrypy</title> <meta http-equiv = " Content-Type" content = " text/html; charset=utf-8" /> <script type = " text/javascript" src = " /media/jquery-1.4.2.min.js"></script> <script type = " text/javascript"> $(function() { // When the testform is submitted... $("#formtest").submit(function() { // post the form values via AJAX... $.post('/submit', {name: $("#name").val()}, function(data) { // and set the title with the result $("#title").html(data['title']) ; }); return false ; }); }); </script> </head> <body> <h1 id = "title">What's your name?</h1> <form id = " formtest" action = " #" method = " post"> <p> <label for = " name">Name:</label> <input type = " text" id = "name" /> <br /> <input type = " submit" value = " Set" /> </p> </form> </body> </html>
AJAX 功能包含在 <script> 标签内。
输出
上面的代码将产生以下输出 -
用户提交值后,AJAX 功能就会实现,屏幕将重定向到表单,如下所示 -
CherryPy - 演示应用程序
在本章中,我们将重点介绍如何在 CherryPy 框架中创建应用程序。
考虑将Photoblog应用程序作为 CherryPy 的演示应用程序。照片博客应用程序是一个普通的博客,但主要文本将是照片而不是文本。Photoblog应用程序的主要优点是开发人员可以更加专注于设计和实现。
基本结构——实体设计
这些实体设计应用程序的基本结构。以下是照片博客应用程序的实体 -
- 电影
- 照片
- 专辑
以下是实体关系的基本类图 -
设计结构
正如上一章所讨论的,该项目的设计结构将如以下屏幕截图所示 -
考虑给定的应用程序,它具有 Photoblog 应用程序的子目录。子目录是 Photo、Album 和 Film,其中包括controllers.py、models.py 和 server.py。
从功能上讲,Photoblog 应用程序将提供 API 来通过传统的 CRUD 接口(创建、检索、更新和删除)操作这些实体。
连接到数据库
存储模块包括一组操作;与数据库的连接是操作之一。
由于它是一个完整的应用程序,API 必须与数据库连接并维护创建、检索、更新和删除的功能。
import dejavu arena = dejavu.Arena() from model import Album, Film, Photo def connect(): conf = {'Connect': "host=localhost dbname=Photoblog user=test password=test"} arena.add_store("main", "postgres", conf) arena.register_all(globals())
上面代码中的 arena 将是底层存储管理器和业务逻辑层之间的接口。
connect 函数将存储管理器添加到 PostgreSQL RDBMS 的 arena 对象。
一旦获得连接,我们就可以根据业务需求创建表单并完成应用程序的工作。
创建任何应用程序之前最重要的事情是实体映射和设计应用程序的结构。
CherryPy - 测试
测试是一个从不同角度进行应用程序的过程,以便 -
- 查找问题列表
- 找出预期结果和实际结果、输出、状态等之间的差异。
- 了解实施阶段。
- 找到对现实目的有用的应用程序。
测试的目的不是让开发人员承担错误,而是提供工具并提高质量来估计应用程序在给定时间的运行状况。
测试需要提前计划。这需要定义测试的目的,了解测试用例的范围,列出业务需求并了解项目不同阶段涉及的风险。
测试被定义为要在系统或应用程序上验证的一系列方面。以下是常见测试方法的列表-
单元测试- 这通常由开发人员自己执行。其目的是检查代码单元是否按预期工作。
可用性测试- 开发人员通常可能会忘记他们正在为不了解系统的最终用户编写应用程序。可用性测试验证产品的优缺点。
功能/验收测试- 可用性测试检查应用程序或系统是否可用,而功能测试则确保每个指定的功能都得到实现。
负载和性能测试- 进行此测试是为了了解系统是否可以适应要进行的负载和性能测试。这可能会导致硬件的改变、优化 SQL 查询等。
回归测试- 它验证产品的连续版本不会破坏以前的任何功能。
可靠性和弹性测试- 可靠性测试有助于通过一个或多个组件的故障来验证系统应用程序。
单元测试
照片博客应用程序不断使用单元测试来检查以下内容 -
- 新功能正常运行并符合预期。
- 新代码的发布不会破坏现有功能。
- 缺陷已修复并保持固定状态。
Python 附带了一个标准的单元测试模块,提供了一种不同的单元测试方法。
单元测试
unittest 植根于 JUnit,这是一个由 Kent Beck 和 Erich Gamma 开发的 Java 单元测试包。单元测试仅返回定义的数据。可以定义模拟对象。这些对象允许针对我们设计的界面进行测试,而无需依赖整个应用程序。它们还提供了一种在隔离模式下运行测试以及包含其他测试的方法。
让我们通过以下方式定义一个虚拟类 -
import unittest class DummyTest(unittest.TestCase): def test_01_forward(self): dummy = Dummy(right_boundary=3) self.assertEqual(dummy.forward(), 1) self.assertEqual(dummy.forward(), 2) self.assertEqual(dummy.forward(), 3) self.assertRaises(ValueError, dummy.forward) def test_02_backward(self): dummy = Dummy(left_boundary=-3, allow_negative=True) self.assertEqual(dummy.backward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.backward(), -3) self.assertRaises(ValueError, dummy.backward) def test_03_boundaries(self): dummy = Dummy(right_boundary=3, left_boundary=-3,allow_negative=True) self.assertEqual(dummy.backward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.forward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.backward(), -3)
代码解释如下 -
应导入unittest模块以为给定类提供单元测试功能。
应该通过子类化unittest来创建一个类。
上面代码中的每个方法都以单词测试开始。所有这些方法都由单元测试处理程序调用。
测试用例调用断言/失败方法来管理异常。
将此视为运行测试用例的示例 -
if __name__ == '__main__': unittest.main()
运行测试用例的结果(输出)如下 -
---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
功能测试
一旦应用程序功能开始按照要求成形,一组功能测试就可以验证应用程序关于规范的正确性。然而,测试应该自动化以获得更好的性能,这需要使用第三方产品,例如 Selenium。
CherryPy 提供了类似内置函数的帮助类来简化功能测试的编写。
负载测试
根据您正在编写的应用程序以及您对数量的期望,您可能需要运行负载和性能测试,以检测应用程序中阻止其达到特定性能水平的潜在瓶颈。
本节不会详细说明如何进行性能或负载测试,因为它不在 FunkLoad 包中。
FunkLoad 的最基本示例如下 -
from funkload.FunkLoadTestCase import FunkLoadTestCase class LoadHomePage(FunkLoadTestCase): def test_homepage(self): server_url = self.conf_get('main', 'url') nb_time = self.conf_getInt('test_homepage', 'nb_time') home_page = "%s/" % server_url for i in range(nb_time): self.logd('Try %i' % i) self.get(home_page, description='Get gome page') if __name__ in ('main', '__main__'): import unittest unittest.main()
这是上面代码的详细解释 -
测试用例必须继承自 FunkLoadTestCase 类,以便 FunkLoad 可以完成其跟踪测试期间发生的情况的内部工作。
类名很重要,因为 FunkLoad 将根据类名查找文件。
设计的测试用例可以直接访问配置文件。只需对服务器调用 Get() 和 post() 方法即可获取响应。
CherryPy - 应用程序的部署
本章将更多地关注通过内置 CherryPy HTTP 服务器启用的基于 CherryPy 的应用程序 SSL。
配置
Web 应用程序需要不同级别的配置设置 -
Web 服务器- 链接到 HTTP 服务器的设置
引擎- 与引擎托管相关的设置
应用程序- 用户使用的应用程序
部署
CherryPy 应用程序的部署被认为是一种非常简单的方法,其中所有必需的包都可以从 Python 系统路径获取。在共享网络托管环境中,网络服务器将驻留在前端,允许主机提供商执行过滤操作。前端服务器可以是 Apache 或lighttpd。
本节将介绍一些在 Apache 和 lighttpd Web 服务器后面运行 CherryPy 应用程序的解决方案。
cherrypy def setup_app(): class Root: @cherrypy.expose def index(self): # Return the hostname used by CherryPy and the remote # caller IP address return "Hello there %s from IP: %s " % (cherrypy.request.base, cherrypy.request.remote.ip) cherrypy.config.update({'server.socket_port': 9091, 'environment': 'production', 'log.screen': False, 'show_tracebacks': False}) cherrypy.tree.mount(Root()) if __name__ == '__main__': setup_app() cherrypy.server.quickstart() cherrypy.engine.start()
SSL协议
基于 CherryPy 的应用程序可以支持SSL(安全套接字层) 。要启用 SSL 支持,必须满足以下要求 -
- 在用户环境中安装 PyOpenSSL 软件包
- 服务器上有 SSL 证书和私钥
创建证书和私钥
让我们处理证书和私钥的要求 -
- 首先,用户需要一个私钥 -
openssl genrsa -out server.key 2048
- 该密钥不受密码保护,因此保护能力较弱。
- 将发出以下命令 -
openssl genrsa -des3 -out server.key 2048
该程序将需要一个密码。如果您的 OpenSSL 版本允许您提供空字符串,请这样做。否则,输入默认密码,然后将其从生成的密钥中删除,如下所示 -
openssl rsa -in server.key -out server.key
- 证书的创建如下 -
openssl req -new -key server.key -out server.csr
此过程将要求您输入一些详细信息。为此,必须发出以下命令 -
openssl x509 -req -days 60 -in server.csr -signkey server.key -out server.crt
新签署的证书有效期为 60 天。
以下代码显示了其实现 -
import cherrypy import os, os.path localDir = os.path.abspath(os.path.dirname(__file__)) CA = os.path.join(localDir, 'server.crt') KEY = os.path.join(localDir, 'server.key') def setup_server(): class Root: @cherrypy.expose def index(self): return "Hello there!" cherrypy.tree.mount(Root()) if __name__ == '__main__': setup_server() cherrypy.config.update({'server.socket_port': 8443, 'environment': 'production', 'log.screen': True, 'server.ssl_certificate': CA, 'server.ssl_private_key': KEY}) cherrypy.server.quickstart() cherrypy.engine.start()
下一步是启动服务器;