基础¶
以下部分将引导您了解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)
注解
登录¶
日志记录是任何应用程序中的一项重要任务。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 formatlog.error_file
对于另一个日志
参见
参考 cherrypy._cplogging
有关Cherrypy日志体系结构的详细信息,请参阅模块。
禁用日志记录¶
您可能有兴趣禁用这两个日志。
要禁用文件日志记录,只需将en空字符串设置为 log.access_file
或 log.error_file
钥匙在你的 global configuration .
禁用、控制台日志记录、设置 log.screen
到 False
.
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")
使用会话¶
会话是开发人员用来识别用户和同步其活动的最常见机制之一。默认情况下,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 7616 和 RFC 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
andREMOTE_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")