基础

以下部分将引导您了解Cherrypy应用程序的基础知识,介绍一些基本概念。

一分钟应用程序示例

用Cherrypy编写的最基本的应用程序几乎涉及到它的所有核心概念。

1import cherrypy
2
3class Root(object):
4    @cherrypy.expose
5    def index(self):
6        return "Hello World!"
7
8if __name__ == '__main__':
9   cherrypy.quickstart(Root(), '/')

首先,对于大多数任务,您永远不需要超过第1行中所示的单个import语句。

在讨论这个问题之前,让我们跳到第9行,该行展示了如何用cherrypy应用服务器托管应用程序,并在 '/' 路径。全部在一条线上。不错。

现在让我们回到实际的应用程序。尽管Cherrypy没有授权它,但大多数时候您的应用程序将被编写为Python类。Cherrypy将调用这些类的方法来响应客户机请求。然而,Cherrypy需要意识到一个方法可以这样使用,我们说这个方法需要 exposed .这正是 cherrypy.expose() 装饰师在第4行。

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

$ python myapp.py

然后将浏览器指向http://127.0.0.1:8080。塔达!

注解

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

简而言之,一旦Cherrypy发现并称之为 exposed 方法,作为开发人员,由您提供实现应用程序逻辑的工具。

Cherrypy认为你,开发人员,最了解。

警告

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

Cherrypy是一个最小的框架,但不是一个简单的框架,它附带了一些基本的工具来涵盖您期望的常见用法。

托管一个或多个应用程序

Web应用程序需要访问HTTP服务器。Cherrypy提供了自己的、生产就绪的HTTP服务器。有两种方法可以用它来承载应用程序。简单的和几乎一样简单的。

单一应用程序

最简单的方法是 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': ... }}

多个应用程序

这个 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() 一个 application ,托管路径段和配置。最后两行只是启动应用服务器。

重要

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

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

注解

你也可以 host foreign WSGI application .

登录

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

为此,Cherrypy管理两个伐木工人:

  • 记录每个传入请求的访问权限

  • 跟踪错误或其他应用程序级消息的应用程序/错误日志

您的应用程序可以通过调用 cherrypy.log() .

cherrypy.log("hello there")

您还可以记录异常:

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

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

  • log.access_file for incoming requests using the common log format

  • log.error_file 对于另一个日志

参见

参考 cherrypy._cplogging 有关Cherrypy日志体系结构的详细信息,请参阅模块。

禁用日志记录

您可能有兴趣禁用这两个日志。

要禁用文件日志记录,只需将en空字符串设置为 log.access_filelog.error_file 钥匙在你的 global configuration .

禁用、控制台日志记录、设置 log.screenFalse .

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

和其他伐木工人一起玩

显然,您的应用程序可能已经使用 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())

在这段代码中,我们创建一个 configuration dictionary 我们传递给 logging 配置记录器的模块:

  • 默认根记录器与单个流处理程序关联

  • 带有单个流处理程序的数据库后端记录器

此外,我们重新配置Cherrypy记录器:

  • 顶层 cherrypy.access 将请求记录到文件中的记录器

  • 这个 cherrypy.error 日志记录器将其他所有内容记录到文件和控制台中

当autoreloader启动时,我们还防止cherrypy试图打开它的日志文件。这不是严格要求的,因为我们一开始甚至不让奇瑞打开它们。但是,这避免了把时间浪费在无用的东西上。

配置

Cherrypy具有细粒度的配置机制,并且可以在不同的级别设置设置。

参见

一旦您回顾了基础知识,请参考 in-depth discussion 围绕配置。

全局服务器配置

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

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

这个 cherrypy.config 对象是字典,更新方法将传递的字典合并到其中。

您也可以传递一个文件(假设 server.conf 文件):

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

警告

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

每个应用程序配置

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

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}

两种方法都有相同的效果,所以选择最适合您风格的方法。

其他应用程序设置

可以添加不特定于请求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(在客户端),必须 send 将过期时间设置为的cookie 0

cherrypy.response.cookie[key] = value
cherrypy.response.cookie[key]['expires'] = 0

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

扩展示例:

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

使用会话

会话是开发人员用来识别用户和同步其活动的最常见机制之一。默认情况下,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 有关会话接口和实现的详细信息,请参阅模块。值得注意的是,您将了解会话过期。

文件系统后端

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

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

memcached后端

Memcached 是一个在RAM之上流行的密钥存储,它是分布式的,如果您想在运行cherrypy的进程之外共享会话,这是一个不错的选择。

要求 Python memcached package 已安装,这可以通过安装来指示 cherrypy[memcached_session] .

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

其他后端

任何其他库都可以实现会话后端。简单的子类 cherrypy.lib.sessions.Session 并指出子类为 tools.sessions.storage_class .

静态内容服务

Cherrypy可以为您的静态内容提供服务,如图像、JavaScript和CSS资源等。

注解

Cherrypy使用 mimetypes 用于确定为特定资源服务的最佳内容类型的模块。如果选择无效,只需按以下方式设置更多媒体类型:

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

为单个文件提供服务

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

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

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

为整个目录服务

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

[/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"

指定索引文件

默认情况下,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/ 通过返回其内容。

允许下载文件

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

例如,您可以编写如下的页面处理程序:

from cherrypy.lib.static import serve_file

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

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

警告

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

处理JSON

Cherrypy内置了对请求和/或响应的JSON编码和解码的支持。

解码请求

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

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

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

编码响应

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

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

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

认证

Cherrypy支持两种非常简单的基于HTTP的身份验证机制,如 RFC 7616RFC 7617 (过时了 RFC 2617 ):基本和摘要。它们通常会触发浏览器弹出窗口,询问用户的姓名和密码。

基本的

基本身份验证是最简单的身份验证形式,但由于用户的凭据嵌入到请求中,因此它不是安全的身份验证。我们建议不要使用它,除非您运行在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调用的函数,该函数传递从请求解码的用户名和密码。

函数可以从它必须的任何源读取数据:文件、数据库、内存等。

摘要

摘要式身份验证的不同之处在于请求没有携带凭证,因此它比基本身份验证更安全。

Cherrypy的Digest支持与上面解释的基本支持具有类似的接口。

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 and REMOTE_USER

  • X_REMOTE_GROUP

腓肠

Cherrypy将自己的甜红色Cherrypy作为默认值。 favicon 使用静态文件工具。您可以按如下方式为自己的腓骨服务:

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

请参阅 static serving 有关详细信息,请参阅。

您还可以使用一个文件来配置它:

[/favicon.ico]
tools.staticfile.on: True
tools.staticfile.filename: "/path/to/myfavicon.ico"
import cherrypy

class HelloWorld(object):
   @cherrypy.expose
   def index(self):
       return "Hello World!"

if __name__ == '__main__':
    cherrypy.quickstart(HelloWorld(), '/', "app.conf")