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 引擎进行处理和编译,以生成一个自包含的 Python 模块,该模块嵌入完整的应用程序及其自身的内置 Web 服务器。

  • 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 与大多数开源项目一样,以软件包的形式提供,可以通过多种方式下载和安装,如下所述:

  • 使用 Tarball
  • 使用 easy_install
  • 使用 Subversion

需求

安装 CherryPy 框架的基本要求包括:

  • Python 版本 2.4 或更高版本
  • CherryPy 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
  • 在 Microsoft Windows 上,
C:\Python or C:\Python2x
  • 在 Mac OS 上,
Python:Lib:site-package

使用 Tarball 安装

Tarball 是文件的压缩存档或目录。CherryPy 框架为其每个版本(alpha、beta 和稳定版)提供一个 Tarball。

它包含库的完整源代码。名称来自在 UNIX 和其他操作系统中使用的实用程序。

以下是使用 tarball 安装 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 企业应用程序工具包 (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.

Web 服务器

它是一个处理 HTTP 协议的接口。其目标是将 HTTP 请求转换为应用程序服务器,以便它们获得响应。

2.

应用程序

它是一段收集信息的软件。

3.

应用程序服务器

它是包含一个或多个应用程序的组件

4.

Web 应用程序服务器

它是 Web 服务器和应用程序服务器的组合。

示例

以下示例显示了 CherryPy 的示例代码:

import cherrypy

class demoExample:
   def index(self):
   return "Hello World!!!"
   index.exposed = True
cherrypy.quickstart(demoExample())

现在让我们了解代码是如何工作的:

  • 名为 CherryPy 的软件包始终在指定的类中导入,以确保正常运行。

  • 在上面的示例中,名为 index 的函数返回参数“Hello World!!!”。

  • 最后一行启动 Web 服务器并调用指定的类(此处为 demoExample),并返回默认函数 index 中提到的值。

示例代码返回以下输出:

Demo Example

内置 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 生成一个 Date 报头字段,该字段用于所有配置。

  • CherryPy 可以处理响应状态代码 (100),并支持客户端。

  • CherryPy 的内置 HTTP 服务器支持持久连接,这是 HTTP/1.1 中的默认设置,通过使用 Connection: Keep-Alive 报头。

  • CherryPy 正确处理分块请求和响应。

  • CherryPy 以两种不同的方式支持请求 - If-Modified-Since 和 If-Unmodified-Since 报头,并根据请求相应地发送响应。

  • CherryPy 允许任何 HTTP 方法。

  • CherryPy 处理客户端和服务器设置之间 HTTP 版本的组合。

多线程应用程序服务器

CherryPy 基于多线程概念设计。每次开发人员在 CherryPy 命名空间中获取或设置值时,都是在多线程环境中完成的。

cherrypy.request 和 cherrypy.response 都是线程数据容器,这意味着您的应用程序可以独立调用它们,并知道在运行时哪些请求通过它们进行代理。

使用线程模式的应用程序服务器并不被高度重视,因为使用线程被认为会增加由于同步要求而导致问题的可能性。

其他替代方案包括 -

多进程模式

每个请求由其自己的 Python 进程处理。在这里,服务器的性能和稳定性可以被认为更好。

异步模式

在这里,接受新连接并将数据发送回客户端与请求进程异步进行。此技术以其效率而闻名。

URL 分发

CherryPy 社区希望更加灵活,并且会感谢其他分发程序的解决方案。CherryPy 3 提供了其他内置分发程序,并提供了一种简单的方法来编写和使用您自己的分发程序。

  • 用于开发 HTTP 方法的应用程序。(GET、POST、PUT 等)
  • 定义 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 参数是连接路由的唯一名称。

  • route 是用于匹配 URI 的模式。

  • controller 是包含页面处理程序的实例。

  • 使用路由分发器连接匹配 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 -

https://127.0.0.1:8080/generate?length=10

步骤 3 - 您将收到以下输出 -

Routes Dispatcher

CherryPy - 工具箱

在 CherryPy 中,内置工具提供了一个调用 CherryPy 库的单一接口。CherryPy 中定义的工具可以通过以下方式实现 -

  • 从配置设置
  • 作为 Python 装饰器或通过页面处理程序的特殊 _cp_config 属性
  • 作为可以从任何函数中应用的 Python 可调用对象

基本身份验证工具

此工具的目的是为应用程序中设计的应用程序提供基本身份验证。

参数

此工具使用以下参数 -

名称 默认值 描述
realm N/A 定义领域值的字符串。
users N/A 形式为用户名:密码的字典或返回此类字典的 Python 可调用函数。
encrypt None Python 可调用对象,用于加密客户端返回的密码并将其与 users 字典中提供的加密密码进行比较。

示例

让我们举一个例子来了解它是如何工作的 -

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 生成的内容的内存缓存。

参数

此工具使用以下参数 -

名称 默认值 描述
invalid_methods ("POST", "PUT", "DELETE") 不应缓存的 HTTP 方法的字符串元组。这些方法还将使资源的任何缓存副本失效(删除)。
cache_Class MemoryCache 要用于缓存的类对象

解码工具

此工具的目的是解码传入的请求参数。

参数

此工具使用以下参数 -

名称 默认值 描述
encoding None 它查找 content-type 标头
Default_encoding "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”,后面跟着给定的名称。

上述代码的输出如下 -

Decoding Tool
hello.html

Decoding Tool Output

CherryPy - 一个工作的应用程序

完整堆栈应用程序提供了一种通过某些命令或执行文件来创建新应用程序的功能。

考虑像 web2py 框架这样的 Python 应用程序;整个项目/应用程序都是根据 MVC 框架创建的。同样,CherryPy 允许用户根据自己的需求设置和配置代码的布局。

在本章中,我们将详细了解如何创建 CherryPy 应用程序并执行它。

文件系统

应用程序的文件系统在以下屏幕截图中显示 -

File System

以下是我们文件系统中各种文件的简要说明 -

  • config.py - 每个应用程序都需要一个配置文件和一种加载它的方法。此功能可以在 config.py 中定义。

  • controllers.py - MVC 是用户遵循的一种流行的设计模式。controllers.py 是所有将安装在cherrypy.tree上的对象实现的地方。

  • models.py - 此文件直接与数据库交互以提供某些服务或存储持久数据。

  • server.py - 此文件与可与负载均衡代理一起正常工作的生产就绪 Web 服务器交互。

  • Static - 它包含所有 CSS 和图像文件。

  • Views - 它包含给定应用程序的所有模板文件。

示例

让我们详细了解创建 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 - 在上述 controllers 代码中,我们有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 文件如下 -

index.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 - 您将收到以下输出 -

File System Output

这里,方法名称定义为“POST”。始终务必交叉验证文件中指定的方法。如果方法包含“POST”方法,则应在数据库中的相应字段中重新检查值。

如果方法包含“GET”方法,则要保存的值将显示在 URL 中。

CherryPy - Web服务

Web 服务是一组基于 Web 的组件,有助于在应用程序或系统之间交换数据,其中还包括开放协议和标准。它可以在 Web 上发布、使用和查找。

Web 服务有多种类型,例如 RWS(RESTfUL Web 服务)、WSDL、SOAP 等。

REST - 表现层状态转移

一种远程访问协议,它将状态从客户端传输到服务器,可用于操纵状态而不是调用远程过程。

  • 没有定义任何特定的编码或结构以及返回有用错误消息的方式。

  • 使用 HTTP“动词”执行状态转移操作。

  • 资源使用 URL 唯一标识。

  • 它不是 API,而是 API 传输层。

REST 保持网络上资源的命名法,并提供统一的机制来对这些资源执行操作。每个资源至少由一个标识符标识。如果 REST 基础设施是使用 HTTP 为基础实现的,则这些标识符被称为统一资源标识符 (URI)

以下是 URI 集的两个常见子集 -

子集 全称 示例
URL 统一资源定位符 http://www.gmail.com/
URN 统一资源名称 urn:isbn:0-201-71088-9 urn: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())

以上代码将产生以下输出:

Error Handling

由于 CherryPy 具有内置的访问工具,因此可以轻松管理 API(应用程序编程接口)。

HTTP 方法

在资源上操作的 HTTP 方法列表如下:

序号 HTTP 方法和操作
1.

HEAD

检索资源元数据。

2.

GET

检索资源元数据和内容。

3.

POST

请求服务器使用请求正文中包含的数据创建新资源。

4.

PUT

请求服务器用请求正文中包含的资源替换现有资源。

5.

DELETE

请求服务器删除由该 URI 标识的资源。

6.

OPTIONS

请求服务器返回有关功能的详细信息,无论是全局的还是针对特定资源的。

Atom 发布协议 (APP)

APP 诞生于 Atom 社区,作为 HTTP 之上的应用程序级协议,允许发布和编辑 Web 资源。APP 服务器和客户端之间消息的单位基于 Atom XML 文档格式。

Atom 发布协议定义了一组 APP 服务和用户代理之间使用 HTTP 及其机制的操作,以及 Atom XML 文档格式作为消息单位。

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 方法 状态码 内容
检索 GET 200 表示资源的 Atom 条目
创建 POST 201 通过 Location 和 Content-Location 标头获取新创建资源的 URI
更新 PUT 200 表示资源的 Atom 条目
删除 DELETE 200 None

CherryPy - 表示层

表示层确保通过它的通信针对预期的接收者。CherryPy 通过各种模板引擎维护表示层的运行。

模板引擎在业务逻辑的帮助下获取页面的输入,然后将其处理到最终页面,该页面仅针对目标受众。

Kid — 模板引擎

Kid 是一款简单的模板引擎,其中包含要处理的模板名称(这是必须的)以及在呈现模板时要传递的数据输入。

首次创建模板时,Kid 会创建一个 Python 模块,该模块可以作为模板的缓存版本提供服务。

kid.Template 函数返回模板类的实例,该实例可用于呈现输出内容。

模板类提供以下命令集:

序号 命令和描述
1.

serialize

它将输出内容作为字符串返回。

2.

generate

它将输出内容作为迭代器返回。

3.

write

它将输出内容转储到文件对象中。

这些命令使用的参数如下:

序号 命令和描述
1.

encoding

它通知如何编码输出内容

2.

fragment

它是一个布尔值,指示是否使用 XML 前言或文档类型声明

3.

output

此类型的序列化用于呈现内容

示例

让我们举一个例子来理解 kid 如何工作:

<!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 的属性

以下是 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 表达式,例如 for value in [...].

示例

以下代码显示了循环机制的工作原理:

<!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')

使用循环机制的上述代码的输出如下:

Looping Output

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 关联数组,并且可以通过以下方式访问 description 字段:

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”的网页,该网页包含在 media 文件夹中。

<!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 Output

用户提交值后,将实现 AJAX 功能,屏幕将重定向到如下所示的表单:

Output Screen

CherryPy - 演示应用程序

在本章中,我们将重点介绍如何在 CherryPy 框架中创建应用程序。

考虑Photoblog 应用程序作为 CherryPy 的演示应用程序。Photoblog 应用程序是一个普通的博客,但主要文本将是照片而不是文本。Photoblog 应用程序的主要重点是开发人员可以更多地关注设计和实现。

基本结构 - 实体设计

实体设计应用程序的基本结构。以下是 Photoblog 应用程序的实体:

  • 胶卷
  • 照片
  • 相册

以下是实体关系的基本类图:

Basic Structure

设计结构

如上一章所述,项目的结构设计将如以下屏幕截图所示:

Design Structure

考虑给定的应用程序,该应用程序具有 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 模块,提供了一种不同的单元测试方法。

单元测试

unittest 源自 JUnit,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 创建一个类。

  • 上面代码中的每个方法都以单词 test 开头。所有这些方法都由 unittest 处理程序调用。

  • 测试用例调用 assert/fail 方法来管理异常。

将此视为运行测试用例的示例:

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 的应用程序,该应用程序通过内置的 CherryPy HTTP 服务器启用 SSL。

配置

Web 应用程序需要不同级别的配置设置:

  • Web 服务器 - 与 HTTP 服务器相关的设置

  • 引擎 - 与引擎托管相关的设置

  • 应用程序 - 用户使用的应用程序

部署

CherryPy 应用程序的部署被认为是一种非常简单的方法,其中所有必需的软件包都可从 Python 系统路径获得。在共享的 Web 托管环境中,Web 服务器将驻留在前端,这允许主机提供商执行过滤操作。前端服务器可以是 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

SSL(安全套接字层)可以在基于 CherryPy 的应用程序中获得支持。要启用 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()

下一步是启动服务器;如果成功,您将在屏幕上看到以下消息:

HTTP Serving HTTPS on https://127.0.0.1:8443/
广告