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 将映射到 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
或
/usr/lib/python2.4/site-packages
  • 在 Microsoft Windows 上,
C:\Python 或 C:\Python2x
  • 在 Mac OS 上,
Python:Lib:site-package

使用安装Tarball

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

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

以下是使用 tar ball − 安装 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 本身上工作时。

  • 用户需要版本控制存储库中主分支的分支时。

  • 用于修复上一版本的错误。

Subversioning 的基本原理是注册一个存储库并跟踪每个版本,其中包括一系列更改。

按照以下步骤了解使用以下工具安装 CherryPy: Subversion−

步骤 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

回溯(最近一次调用最后一次):
文件"<stdin>",第 1 行,在?
ImportError:没有名为 cherrypy 的模块

CherryPy - 词汇表

需要定义几个重要的关键字才能理解 CherryPy 的工作原理。关键字和定义如下 −

S.No 关键字 &定义
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 服务器通过使用 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 方法的应用程序。 (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

Routes Dispatcher

以下是分派 − 所需方法的参数列表

  • name 参数是要连接的路由的唯一名称。

  • 路由是匹配 URI 的模式。

  • 控制器是包含页面处理程序的实例。

  • 使用 Routes 调度程序连接匹配 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 − 您将收到以下输出 −

Routes Dispatcher

CherryPy - 工具箱

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

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

基本身份验证工具

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

参数

此工具使用以下参数 −

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

示例

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

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 函数返回一个硬编码的字典,但也从数据库或其他任何地方获取值。类管理员包含此功能,它使用 CherryPy 的身份验证内置工具。身份验证会加密密码和用户 ID。

基本身份验证工具并不真正安全,因为入侵者可以对密码进行编码和解码。

缓存工具

此工具的目的是提供 CherryPy 生成内容的内存缓存。

参数

此工具使用以下参数 −

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

解码工具

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

参数

此工具使用以下参数 −

名称 默认 描述
encoding None 它查找内容类型标题
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"。

上述代码的输出如下 −

解码工具
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 文件如下 −

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

文件系统输出

此处,方法名称定义为"POST"。交叉验证文件中指定的方法始终很重要。如果方法包括"POST"方法,则应在数据库中的相应字段中重新检查值。

如果方法包括"GET"方法,则要保存的值将在 URL 中可见。

CherryPy - Web 服务

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

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

REST — 表述性状态转移

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

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

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

  • 使用 URL 唯一标识资源。

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

REST 维护网络上资源的命名法,并提供统一的机制来对这些资源执行操作。每个资源至少由一个标识符标识。如果 REST 基础架构以 HTTP 为基础实现,则这些标识符称为统一资源标识符 (URI)

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

子集 完整形式 示例
URL Uniform Resource Locator http://www.gmail.com/
URN Uniform Resource Name 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 方法列表如下 −

S.No HTTP 方法和操作
1.

HEAD

检索资源元数据。

2.

GET

检索资源元数据和内容。

3.

POST

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

4.

PUT

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

5.

DELETE

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

6.

OPTIONS

请求服务器返回有关全局或特定资源功能的详细信息。

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 方法 状态代码 内容
Retrieve GET 200 代表资源的 Atom 条目
Create POST 201 通过 Location 和 Content-Location 标头获取新创建资源的 URI
Update PUT 200 代表资源的 Atom 条目
Delete DELETE 200 None

CherryPy - 表示层

表示层确保通过它的通信能够到达预期的接收者。 CherryPy 通过各种模板引擎来维护表示层的工作。

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

Kid — 模板引擎

Kid 是一个简单的模板引擎,它包括要处理的模板的名称(这是强制性的)和呈现模板时要传递的数据输入。

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

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

模板类提供以下命令集 −

S.No 命令 &描述
1.

serialize

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

2.

generate

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

3.

write

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

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

S.No 命令和说明
1.

encoding

它告知如何对输出内容进行编码

2.

fragment

它是一个布尔值,告知 XML prolog 或 Doctype

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

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

循环输出

CherryPy - Ajax 的使用

直到 2005 年,所有 Web 应用程序遵循的模式都是每页管理一个 HTTP 请求。从一个页面导航到另一个页面需要加载整个页面。这会在更大程度上降低性能。

因此,富客户端应用程序的兴起,这些应用程序曾经嵌入 AJAX、XML 和 JSON。

AJAX

异步 ​​JavaScript 和 XML (AJAX) 是一种创建快速动态网页的技术。AJAX 允许通过在后台与服务器交换少量数据来异步更新网页。这意味着可以更新网页的部分内容,而无需重新加载整个页面。

Google Maps、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 输出

一旦用户提交值,AJAX 功能就会实现,屏幕将重定向到如下所示的表单 −

输出屏幕

CherryPy - 演示应用程序

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

Photoblog 应用程序视为 CherryPy 的演示应用程序。Photoblog 应用程序是一个普通的博客,但主要文本将是照片而不是文本。 Photoblog 应用程序的主要优势在于开发人员可以更加专注于设计和实现。

基本结构 - 实体设计

实体设计应用程序的基本结构。以下是 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())

上述代码中的竞技场将成为底层存储管理器和业务逻辑层之间的接口。

connect 函数将存储管理器添加到 PostgreSQL RDBMS 的竞技场对象。

一旦获得连接,我们就可以根据业务需求创建表单并完成应用程序的工作。

在创建任何应用程序之前,最重要的是实体映射和设计应用程序的结构。

CherryPy - 测试

测试是一个从不同角度进行应用程序的过程,以便−

  • 查找问题列表
  • 查找预期结果和实际结果、输出、状态等之间的差异。
  • 了解实施阶段。
  • 找到对实际目的有用的应用程序。

目标测试的目的不是让开发人员承担责任,而是提供工具并提高质量,以评估应用程序在特定时间的健康状况。

测试需要提前规划。这要求定义测试的目的、了解测试用例的范围、列出业务需求并了解项目不同阶段所涉及的风险。

测试被定义​​为要在系统或应用程序上验证的一系列方面。以下是常见测试方法的列表 −

  • 单元测试 − 这通常由开发人员自己执行。这旨在检查代码单元是否按预期工作。

  • 可用性测试 − 开发人员通常会忘记他们正在为不了解系统的最终用户编写应用程序。可用性测试可验证产品的优缺点。

  • 功能/验收测试 − 可用性测试可检查应用程序或系统是否可用,而功能测试可确保实现每个指定的功能。

  • 负载和性能测试 − 执行此测试是为了了解系统是否可以适应要进行的负载和性能测试。这可能导致硬件更改、优化 SQL 查询等。

  • 回归测试 − 它可验证产品的连续发布不会破坏任何先前的功能。

  • 可靠性和弹性测试 −可靠性测试有助于验证系统应用程序的一个或多个组件是否发生故障。

单元测试

Photoblog 应用程序不断使用单元测试来检查以下内容 −

  • 新功能正常运行,符合预期。
  • 新代码发布不会破坏现有功能。
  • 缺陷已修复并保持修复状态。

Python 附带标准 unittest 模块,提供不同的单元测试方法。

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 HTTP 服务器启用的基于 CherryPy 的应用程序 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

基于 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()

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

HTTP Serving HTTPS on https://localhost:8443/