基础CherryPy

2 年前

Basics

以下各节将引导您了解CherryPy应用程序的基础知识,并介绍一些基本概念。

内容

The one-minute application example

您可以使用CherryPy编写的最基本的应用程序几乎涉及其所有核心概念。

1
import cherrypy
class Root(object):
    @cherrypy.expose
    def index(self):
        return "Hello World!"
if __name__ == '__main__':
   cherrypy.quickstart(Root(), '/')

首先,对于大多数任务,您将只需要一条导入语句即可,如第1行所示。

在讨论这些内容之前,让我们跳到第9行,该行显示了如何使用CherryPy应用程序服务器托管应用程序,以及如何在'/'路径中通过内置的HTTP服务器为其提供服务。统一在一行中。不错。

现在让我们回到实际的应用程序。即使CherryPy没有强制要求,大多数时候您的应用程序仍将被编写为Python类。这些类的方法将由CherryPy调用以响应客户端请求。但是,CherryPy需要意识到可以使用这种方法,我们说该方法需要 公开 。这正是 cherrypy.expose() 装饰器在第4行中所做的。

将代码段保存在名为myapp.py的文件中,然后运行您的第一个CherryPy应用程序:

$ python myapp.py

然后将浏览器指向 http://127.0.0.1:8080 。多田!

注意

CherryPy是一个小型框架,专注于一项任务:接收HTTP请求并找到与请求的URL匹配的最合适的Python函数或方法。与其他知名框架不同,CherryPy不提供对数据库访问,HTML模板或任何其他中间件漂亮功能的内置支持。

简而言之,一旦CherryPy找到并调用了 公开的 方法,作为开发人员,您便有责任提供实现应用程序逻辑的工具。

CherryPy认为您(开发人员)最了解。

警告

前面的示例演示了CherryPy接口的简单性,但是您的应用程序可能还会包含其他一些细节:静态服务,更复杂的结构,数据库访问等。这将在教程部分中进行开发。

CherryPy是一个最小的框架,但不是一个裸露的框架,它带有一些基本工具来涵盖您期望的常用用法。

Hosting one or more applications

Web应用程序需要访问HTTP服务器。CherryPy提供了自己的,可投入生产的HTTP服务器。有两种方法来托管应用程序。简单的和几乎简单的一个。

Single application

最直接的方法是使用 cherrypy.quickstart() 函数。它需要至少一个参数,即要托管的应用程序实例。另外两个设置是可选的。首先,可以从中访问应用程序的基本路径。其次,使用配置字典或文件来配置您的应用程序。

cherrypy.quickstart(Blog())
cherrypy.quickstart(Blog(), '/blog')
cherrypy.quickstart(Blog(), '/blog', {'/': {'tools.gzip.on': True}})

第一个意味着您的应用程序将在 http:// hostname:port / 可用, 而另两个将使您的博客应用程序在 http:// hostname:port / blog 可用。此外,最后一个为应用程序提供了特定设置。

注意

注意在第三种情况下,设置如何仍然相对于应用程序,而不是在何处可用,因此使用{'/':…}而不是{'/ blog':…}

Multiple applications

cherrypy.quickstart() 方法对于单个应用程序来说很好,但是缺乏用服务器托管多个应用程序的能力。为此,必须使用以下 cherrypy.tree.mount 功能:

cherrypy.tree.mount(Blog(), '/blog', blog_conf)
cherrypy.tree.mount(Forum(), '/forum', forum_conf)
cherrypy.engine.start()
cherrypy.engine.block()

本质上, cherrypy.tree.mount 它采用与以下相同的参数 cherrypy.quickstart() :一个 应用程序 ,一个主机路径段和一个配置。最后两行只是启动应用程序服务器。

重要

cherrypy.quickstart() cherrypy.tree.mount 不是排他的。例如,前几行可以写成:

cherrypy.tree.mount(Blog(), '/blog', blog_conf)
cherrypy.quickstart(Forum(), '/forum', forum_conf)

注意

您还可以 托管外国WSGI应用程序

Logging

日志记录是任何应用程序中的重要任务。CherryPy将记录所有传入的请求以及协议错误。

为此,CherryPy管理着两个记录器:

  • 记录每个传入请求的访问权限
  • 跟踪错误或其他应用程序级别消息的应用程序/错误日志

您的应用程序可以通过调用来使用第二个记录器 cherrypy.log()

cherrypy.log("hello there")

您还可以记录异常:

try:
except Exception:
   cherrypy.log("kaboom!", traceback=True)

这两个日志都将写入由配置中的以下键标识的文件:

也可以看看

请参阅该 cherrypy._cplogging 模块以获取有关CherryPy的日志记录体系结构的更多详细信息。

Disable logging

您可能有兴趣禁用任一日志。

要禁用文件记录,只需在 全局配置中 log.access_file log.error_file 键 设置一个空字符串 。

要禁用控制台日志记录,请设置 log.screen 为False。

cherrypy.config.update({'log.screen': False,
                        'log.access_file': '',
                        'log.error_file': ''})

Play along with your other loggers

您的应用程序可能显然已经在使用该 logging 模块来跟踪应用程序级别的消息。下面是一个简单的设置示例。

import logging
import logging.config
import cherrypy
logger = logging.getLogger()
db_logger = logging.getLogger('db')
LOG_CONF = {
    'version': 1,
    'formatters': {
        'void': {
            'format': ''
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
    'handlers': {
        'default': {
            'level':'INFO',
            'class':'logging.StreamHandler',
            'formatter': 'standard',
            'stream': 'ext://sys.stdout'
        'cherrypy_console': {
            'level':'INFO',
            'class':'logging.StreamHandler',
            'formatter': 'void',
            'stream': 'ext://sys.stdout'
        'cherrypy_access': {
            'level':'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'void',
            'filename': 'access.log',
            'maxBytes': 10485760,
            'backupCount': 20,
            'encoding': 'utf8'
        'cherrypy_error': {
            'level':'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'void',
            'filename': 'errors.log',
            'maxBytes': 10485760,
            'backupCount': 20,
            'encoding': 'utf8'
    'loggers': {
        '': {
            'handlers': ['default'],
            'level': 'INFO'
        'db': {
            'handlers': ['default'],
            'level': 'INFO' ,
            'propagate': False
        'cherrypy.access': {
            'handlers': ['cherrypy_access'],
            'level': 'INFO',
            'propagate': False
        'cherrypy.error': {
            'handlers': ['cherrypy_console', 'cherrypy_error'],
            'level': 'INFO',
            'propagate': False
class Root(object):
    @cherrypy.expose
    def index(self):
        logger.info("boom")
        db_logger.info("bam")
        cherrypy.log("bang")
        return "hello world"
if __name__ == '__main__':
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': ''})
cherrypy.engine.unsubscribe('graceful', cherrypy.log.reopen_files)
    logging.config.dictConfig(LOG_CONF)
    cherrypy.quickstart(Root())

在此代码段中,我们创建一个 配置字典 ,然后将其传递给 logging 模块以配置记录器:

  • 默认的根记录器与单个流处理程序关联
  • db后端的记录器,还有一个流处理程序

另外,我们重新配置CherryPy记录器:

  • 顶级 cherrypy.access 记录器,将请求记录到文件中
  • cherrypy.error 记录器来登录其他一切文件和控制台

当自动重新加载程序启动时,我们还阻止CherryPy尝试打开其日志文件。由于我们甚至都不让CherryPy首先打开它们,因此这不是严格要求的。但是,这样可以避免浪费时间在无用的东西上。

Configuring

CherryPy带有细粒度的配置机制,可以在各种级别上进行设置。

也可以看看

复习基础知识后,请参考 有关 配置 的深入讨论

Global server configuration

若要配置HTTP和应用程序服务器,请使用 cherrypy.config.update() 方法。

cherrypy.config.update({'server.socket_port': 9090})

cherrypy.config 对象是字典,并且update方法将传递的字典合并到其中。

您还可以改为传递文件(假设使用server.conf 文件):

[global]
server.socket_port: 9090
cherrypy.config.update("server.conf")

警告

cherrypy.config.update() 不用于配置应用程序。这是一个常见的错误。它用于配置服务器和引擎。

Per-application configuration

要配置您的应用程序,请在将应用程序与服务器关联时传入字典或文件。

cherrypy.quickstart(myapp, '/', {'/': {'tools.gzip.on': True}})

或通过文件(例如称为app.conf):

[/]
tools.gzip.on: True
cherrypy.quickstart(myapp, '/', "app.conf")

尽管您可以全局方式定义大多数配置,但有时在代码中应用它们的位置定义它们很方便。

class Root(object):
    @cherrypy.expose
    @cherrypy.tools.gzip()
    def index(self):
        return "hello world!"

上面的变体符号:

class Root(object):
    @cherrypy.expose
    def index(self):
        return "hello world!"
    index._cp_config = {'tools.gzip.on': True}

两种方法具有相同的效果,因此请选择最适合您的样式的方法。

Additional application settings

您可以添加非特定于请求URL的设置,并从页面处理程序中检索它们,如下所示:

[/]
tools.gzip.on: True
[googleapi]
key = "..."
appid = "..."
class Root(object):
    @cherrypy.expose
    def index(self):
        google_appid = cherrypy.request.app.config['googleapi']['appid']
        return "hello world!"
cherrypy.quickstart(Root(), '/', "app.conf")

Cookies

CherryPy使用 Cookie python中的模块,尤其是使用 Cookie.SimpleCookie 对象类型来处理cookie。

  • 要将Cookie发送到浏览器,请设置。 cherrypy.response.cookie[key] = value
  • 要检索浏览器发送的cookie,请使用 cherrypy.request.cookie[key]
  • 要删除Cookie(在客户端),您必须 发送 有效期设置为0的Cookie :
cherrypy.response.cookie[key] = value
cherrypy.response.cookie[key]['expires'] = 0

重要的是要了解请求cookie 不会 自动复制到响应cookie。客户将在每个请求上发送相同的cookie,因此 cherrypy.request.cookie 每次都应填充。但是服务器不需要在每个响应中都发送相同的cookie。因此, cherrypy.response.cookie 通常为空。因此,当您希望“删除”(过期)cookie时,必须先进行设置 ,然后将其属性设置为0。 cherrypy.response.cookie[key] = valueexpires

扩展示例:

import cherrypy
class MyCookieApp(object):
    @cherrypy.expose
    def set(self):
        cookie = cherrypy.response.cookie
        cookie['cookieName'] = 'cookieValue'
        cookie['cookieName']['path'] = '/'
        cookie['cookieName']['max-age'] = 3600
        cookie['cookieName']['version'] = 1
        return "<html><body>Hello, I just sent you a cookie</body></html>"
    @cherrypy.expose
    def read(self):
        cookie = cherrypy.request.cookie
        res = """<html><body>Hi, you sent me %s cookies.<br />
                Here is a list of cookie names/values:<br />""" % len(cookie)
        for name in cookie.keys():
            res += "name: %s, value: %s<br>" % (name, cookie[name].value)
        return res + "</body></html>"
if __name__ == '__main__':
    cherrypy.quickstart(MyCookieApp(), '/cookie')

Using sessions

会话是开发人员用来识别用户并同步其活动的最常用机制之一。默认情况下,CherryPy不激活会话,因为它不是必需的功能,要使其启用,只需在配置中添加以下设置:

[/]
tools.sessions.on: True
cherrypy.quickstart(myapp, '/', "app.conf")

会话默认情况下存储在RAM中,因此,如果重新启动服务器,则所有当前会话都将丢失。您可以将它们存储在memcached或文件系统中。

在应用程序中使用会话的操作如下:

import cherrypy
@cherrypy.expose
def index(self):
    if 'count' not in cherrypy.session:
       cherrypy.session['count'] = 0
    cherrypy.session['count'] += 1

在此代码段中,每次调用索引页面处理程序时,当前用户的会话的“ count”键都增加1。

CherryPy通过检查与请求一起发送的cookie来知道要使用哪个会话。该cookie包含CherryPy用于从存储中加载用户会话的会话标识符。

也可以看看

cherrypy.lib.sessions 有关会话接口和实现的更多详细信息,请参考该模块。值得注意的是,您将了解会话到期。

Filesystem backend

使用文件系统很简单,不会在重新启动之间丢失会话。每个会话都保存在给定目录中的自己的文件中。

[/]
tools.sessions.on: True
tools.sessions.storage_class = cherrypy.lib.sessions.FileSession
tools.sessions.storage_path = "/some/directory"

Memcached backend

Memcached 是RAM上流行的密钥库,它是分布式的,如果要在运行CherryPy的进程之外共享会话,它是一个不错的选择。

要求已安装Python memcached软件包 ,可通过install指示 cherrypy[memcached_session]

[/]
tools.sessions.on: True
tools.sessions.storage_class = cherrypy.lib.sessions.MemcachedSession

Other backends

任何其他库都可以实现会话后端。只需将其子类化, cherrypy.lib.sessions.Session 并将其表示为即可 tools.sessions.storage_class

Static content serving

CherryPy可以提供您的静态内容,例如图像,javascript和CSS资源等。

注意

CherryPy使用该 mimetypes 模块来确定服务特定资源的最佳内容类型。如果选择无效,则可以如下设置更多的媒体类型:

import mimetypes
mimetypes.types_map['.csv'] = 'text/csv'

Serving a single file

您可以按以下方式提供单个文件:

[/style.css]
tools.staticfile.on = True
tools.staticfile.filename = "/home/site/style.css"

CherryPy将自动响应URL,例如 http://hostname/style.css。

Serving a whole directory

服务整个目录类似于单个文件:

[/static]
tools.staticdir.on = True
tools.staticdir.dir = "/home/site/static"

假设您在static / js / my.js中有一个文件,CherryPy将自动响应URL,例如 http://hostname/static/js/my.js。

注意

CherryPy始终需要将要服务的文件或目录的绝对路径。如果要配置多个静态部分,但它们位于同一根目录中,则可以使用以下快捷方式:

[/]
tools.staticdir.root = "/home/site"
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"

Specifying an index file

默认情况下,CherryPy将以404错误响应静态目录的根,指示未找到路径“ /”。要指定索引文件,可以使用以下命令:

[/static]
tools.staticdir.on = True
tools.staticdir.dir = "/home/site/static"
tools.staticdir.index = "index.html"

假设您在static / index.html上有一个文件,CherryPy将通过返回其内容自动响应URL,例如 http:// hostname / static /。

Allow files downloading

使用 "application/x-download" 响应内容类型,您可以告诉浏览器应该将资源下载到用户的计算机上而不是显示。

例如,您可以编写一个页面处理程序,如下所示:

from cherrypy.lib.static import serve_file
@cherrypy.expose
def download(self, filepath):
    return serve_file(filepath, "application/x-download", "attachment")

假设文件路径是您计算机上的有效路径,那么浏览器会将响应视为可下载的内容。

警告

上面的页面处理程序本身就有安全风险,因为可以访问服务器的任何文件(如果运行服务器的用户对其具有权限)。

Dealing with JSON

CherryPy具有对请求和/或响应的JSON编码和解码的内置支持。

Decoding request

要使用JSON自动解码请求的内容,请执行以下操作:

class Root(object):
    @cherrypy.expose
    @cherrypy.tools.json_in()
    def index(self):
        data = cherrypy.request.json

附加到请求的json属性包含解码后的内容。

Encoding response

要使用JSON自动编码响应的内容,请执行以下操作:

class Root(object):
    @cherrypy.expose
    @cherrypy.tools.json_out()
    def index(self):
        return {'key': 'value'}

CherryPy将使用JSON对您的页面处理程序返回的所有内容进行编码。并非所有类型的对象都可以本地编码。

Authentication

CherryPy提供了对两种非常简单的基于HTTP的身份验证机制的支持,如以下所述 RFC 7616 RFC 7617 (已过时 RFC 2617 ):基本和摘要。众所周知,它们会触发浏览器的弹出窗口,询问用户其名称和密码。

Basic

基本身份验证是最简单的身份验证形式,但是由于用户的凭据已嵌入到请求中,因此它不是安全的形式。我们建议不要使用它,除非您在SSL上或封闭的网络中运行。

from cherrypy.lib import auth_basic
USERS = {'jon': 'secret'}
def validate_password(realm, username, password):
    if username in USERS and USERS[username] == password:
       return True
    return False
conf = {
   '/protected/area': {
       'tools.auth_basic.on': True,
       'tools.auth_basic.realm': 'localhost',
       'tools.auth_basic.checkpassword': validate_password,
       'tools.auth_basic.accept_charset': 'UTF-8',
cherrypy.quickstart(myapp, '/', conf)

简而言之,您必须提供一个由CherryPy调用的函数,该函数传递从请求中解码的用户名和密码。

该函数可以从其必须具有的任何源读取数据:文件,数据库,内存等。

Digest

摘要式身份验证的不同之处在于,凭据不是由请求执行的,因此它比基本身份验证更为安全。

CherryPy的摘要支持具有与上述基本支持类似的界面。

from cherrypy.lib import auth_digest
USERS = {'jon': 'secret'}
conf = {
   '/protected/area': {
        'tools.auth_digest.on': True,
        'tools.auth_digest.realm': 'localhost',
        'tools.auth_digest.get_ha1': auth_digest.get_ha1_dict_plain(USERS),
        'tools.auth_digest.key': 'a565c27146791cfb',
        'tools.auth_digest.accept_charset': 'UTF-8',
cherrypy.quickstart(myapp, '/', conf)

SO_PEERCRED

UNIX文件和抽象套接字还有一个低级身份验证。这是您启用它的方式:

[global]
server.peercreds: True
server.peercreds_resolve: True
server.socket_file: /var/run/cherrypy.sock

server.peercreds 启用查找连接的进程ID,用户ID和组ID的功能。它们将作为WSGI环境变量进行访问:

  • X_REMOTE_PID
  • X_REMOTE_UID
  • X_REMOTE_GID

server.peercreds_resolve 将其解析为用户名和组名。它们将作为WSGI环境变量进行访问:

  • X_REMOTE_USER REMOTE_USER
  • X_REMOTE_GROUP

Favicon

CherryPy 使用静态文件工具将其自己的甜红色cherrypy作为默认 图标 提供服务 。您可以按以下方式提供自己的网站图标:

import cherrypy
class HelloWorld(object):
   @cherrypy.expose
   def index(self):
       return "Hello World!"
if __name__ == '__main__':
    cherrypy.quickstart(HelloWorld(), '/',
            '/favicon.ico':
                'tools.staticfile.on': True,
                'tools.staticfile.filename': '/path/to/myfavicon.ico'
    )

有关更多详细信息,请参阅 静态投放 部分。

您还可以使用文件进行配置:

[/favicon.ico]
tools.staticfile.on: True
tools.staticfile.filename: "/path/to/myfavicon.ico"
import cherrypy
class HelloWorld(object):