基礎?

以下部分將引導您了解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??蛻魴C將在每次請求時發送相同的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")