基础CherryPy
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)
这两个日志都将写入由配置中的以下键标识的文件:
-
log.access_file
使用 通用日志格式的 传入请求 -
log.error_file
对于其他日志
也可以看看
请参阅该
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):