- CherryPy 教程
- CherryPy - 首頁
- CherryPy - 簡介
- CherryPy - 環境設定
- CherryPy - 詞彙表
- 內建HTTP伺服器
- CherryPy - 工具箱
- CherryPy - 一個工作的應用程式
- CherryPy - Web服務
- CherryPy - 表示層
- CherryPy - Ajax的使用
- CherryPy - 演示應用程式
- CherryPy - 測試
- 應用程式的部署
- CherryPy 有用資源
- CherryPy 快速指南
- CherryPy - 有用資源
- CherryPy - 討論
CherryPy 快速指南
CherryPy - 簡介
CherryPy 是一個 Python 的 Web 框架,它為 Python 開發人員提供了一個友好的 HTTP 協議介面。它也被稱為 Web 應用程式庫。
CherryPy 利用 Python 作為動態語言的優勢,將 HTTP 協議建模並繫結到 API 中。它是 Python 最古老的 Web 框架之一,它提供乾淨的介面和可靠的平臺。
CherryPy 的歷史
Remi Delon 於 2002 年 6 月底釋出了 CherryPy 的第一個版本。這是這個成功的 Python Web 庫的起點。Remi 是一位法國駭客,他信任 Python 是 Web 應用程式開發的最佳選擇之一。
Remi 開發的專案吸引了許多對這種方法感興趣的開發人員。這種方法包括以下特性:
CherryPy 接近模型-檢視-控制器模式。
CherryPy 類必須由 CherryPy 引擎進行處理和編譯,以生成一個自包含的 Python 模組,該模組嵌入完整的應用程式及其自身的內建 Web 伺服器。
CherryPy 可以將 URL 及其查詢字串對映到 Python 方法呼叫,例如:
http://somehost.net/echo?message=hello would map to echo(message='hello')
在 CherryPy 專案的兩年開發過程中,它得到了社群的支援,Remi 釋出了幾個改進版本。
2004 年 6 月,關於該專案的未來以及是否應繼續使用相同的架構展開了討論。隨後,幾位專案常客的集思廣益和討論導致了物件釋出引擎和過濾器的概念,很快成為 CherryPy2 的核心部分。後來,在 2004 年 10 月,第一個 CherryPy 2 alpha 版本作為這些核心思想的概念驗證釋出。CherryPy 2.0 取得了真正的成功;但是,人們認識到它的設計仍然可以改進,並且需要重構。
在基於反饋的討論之後,CherryPy 的 API 進行了進一步修改以提高其優雅性,最終在 2005 年 10 月釋出了 CherryPy 2.1.0。經過各種修改,團隊於 2006 年 4 月釋出了 CherryPy 2.2.0。
CherryPy 的優勢
CherryPy 的以下特性被認為是其優勢:
簡單性
在 CherryPy 中開發專案是一項簡單的任務,只需根據 Python 的約定和縮排開發少量程式碼。
CherryPy 也很模組化。主要元件透過正確的邏輯概念得到良好管理,並且父類可以擴充套件到子類。
強大
CherryPy 利用了 Python 的全部功能。它還提供工具和外掛,這些是開發世界級應用程式所需的強大擴充套件點。
開源
CherryPy 是一個開源的 Python Web 框架(根據開源 BSD 許可證許可),這意味著該框架可以免費用於商業用途。
社群幫助
它擁有一個敬業的社群,該社群為各種型別的問題和答案提供完整的支援。社群試圖為從初學者到高級別的開發人員提供全面的幫助。
部署
有一些經濟高效的方式來部署應用程式。CherryPy 包含自己的生產就緒 HTTP 伺服器來託管您的應用程式。CherryPy 也可以部署在任何符合 WSGI 的閘道器上。
CherryPy - 環境設定
CherryPy 與大多數開源專案一樣,以軟體包的形式提供,可以透過多種方式下載和安裝,如下所述:
- 使用 Tarball
- 使用 easy_install
- 使用 Subversion
需求
安裝 CherryPy 框架的基本要求包括:
- Python 版本 2.4 或更高版本
- CherryPy 3.0
安裝 Python 模組被認為是一個簡單的過程。安裝包括使用以下命令。
python setup.py build python setup.py install
Python 的軟體包儲存在以下預設目錄中:
- 在 UNIX 或 Linux 上,
/usr/local/lib/python2.4/site-packages or /usr/lib/python2.4/site-packages
- 在 Microsoft Windows 上,
C:\Python or C:\Python2x
- 在 Mac OS 上,
Python:Lib:site-package
使用 Tarball 安裝
Tarball 是檔案的壓縮存檔或目錄。CherryPy 框架為其每個版本(alpha、beta 和穩定版)提供一個 Tarball。
它包含庫的完整原始碼。名稱來自在 UNIX 和其他作業系統中使用的實用程式。
以下是使用 tarball 安裝 CherryPy 的步驟:
步驟 1 - 從 http://download.cherrypy.org/ 下載使用者需求的版本。
步驟 2 - 搜尋 Tarball 下載到的目錄並解壓縮它。對於 Linux 作業系統,鍵入以下命令:
tar zxvf cherrypy-x.y.z.tgz
對於 Microsoft Windows,使用者可以使用 7-Zip 或 Winzip 等實用程式透過圖形介面解壓縮存檔。
步驟 3 - 移動到新建立的目錄並使用以下命令構建 CherryPy:
python setup.py build
對於全域性安裝,應使用以下命令:
python setup.py install
使用 easy_install 安裝
Python 企業應用程式工具包 (PEAK) 提供了一個名為 Easy Install 的 python 模組。這有助於部署 Python 軟體包。此模組簡化了下載、構建和部署 Python 應用程式和產品的過程。
在安裝 CherryPy 之前,需要在系統中安裝 Easy Install。
步驟 1 - 從 http://peak.telecommunity.com 下載 ez_setup.py 模組,並使用計算機上的管理員許可權執行它:python ez_setup.py。
步驟 2 - 使用以下命令安裝 Easy Install。
easy_install product_name
步驟 3 - easy_install 將搜尋 Python 軟體包索引 (PyPI) 以查詢給定的產品。PyPI 是所有 Python 產品資訊的集中儲存庫。
使用以下命令部署 CherryPy 的最新可用版本:
easy_install cherrypy
步驟 4 - 然後,easy_install 將下載 CherryPy、構建並將其全域性安裝到您的 Python 環境中。
使用 Subversion 安裝
在以下情況下建議使用 Subversion 安裝 CherryPy:
存在功能或已修復錯誤,並且僅在開發中的程式碼中可用。
當開發人員處理 CherryPy 本身時。
當用戶需要版本控制儲存庫中主分支的分支時。
用於修復先前版本的錯誤。
版本控制的基本原理是註冊一個儲存庫並跟蹤每個版本,其中包括一系列更改。
按照以下步驟瞭解如何使用 Subversion 安裝 CherryPy:
步驟 1 - 要使用專案的最新版本,需要檢出在 Subversion 儲存庫中找到的 trunk 資料夾。
步驟 2 - 從 shell 中輸入以下命令:
svn co http://svn.cherrypy.org/trunk cherrypy
步驟 3 - 現在,建立一個 CherryPy 目錄並將完整的原始碼下載到其中。
測試安裝
需要驗證應用程式是否已正確安裝到系統中,就像我們對 Java 等應用程式所做的那樣。
您可以選擇上一章中提到的三種方法中的任何一種在您的環境中安裝和部署 CherryPy。CherryPy 必須能夠從 Python shell 中匯入,如下所示:
import cherrypy cherrypy.__version__ '3.0.0'
如果 CherryPy 沒有全域性安裝到本地系統的 Python 環境中,則需要設定 PYTHONPATH 環境變數,否則它將以以下方式顯示錯誤:
import cherrypy Traceback (most recent call last): File "<stdin>", line 1, in ? ImportError: No module named cherrypy
CherryPy - 詞彙表
為了理解 CherryPy 的工作原理,需要定義一些重要的關鍵字。關鍵字及其定義如下:
| 序號 | 關鍵字和定義 |
|---|---|
| 1. | Web 伺服器 它是一個處理 HTTP 協議的介面。其目標是將 HTTP 請求轉換為應用程式伺服器,以便它們獲得響應。 |
| 2. | 應用程式 它是一段收集資訊的軟體。 |
| 3. | 應用程式伺服器 它是包含一個或多個應用程式的元件 |
| 4. | Web 應用程式伺服器 它是 Web 伺服器和應用程式伺服器的組合。 |
示例
以下示例顯示了 CherryPy 的示例程式碼:
import cherrypy class demoExample: def index(self): return "Hello World!!!" index.exposed = True cherrypy.quickstart(demoExample())
現在讓我們瞭解程式碼是如何工作的:
名為 CherryPy 的軟體包始終在指定的類中匯入,以確保正常執行。
在上面的示例中,名為 index 的函式返回引數“Hello World!!!”。
最後一行啟動 Web 伺服器並呼叫指定的類(此處為 demoExample),並返回預設函式 index 中提到的值。
示例程式碼返回以下輸出:
內建 HTTP 伺服器和內部引擎
CherryPy 自帶 Web(HTTP)伺服器。這就是為什麼 CherryPy 是自包含的,並允許使用者在獲取庫後幾分鐘內執行 CherryPy 應用程式。
Web 伺服器充當應用程式的閘道器,藉助該閘道器,所有請求和響應都得到跟蹤。
要啟動 Web 伺服器,使用者必須進行以下呼叫:
cherryPy.server.quickstart()
CherryPy 的內部引擎負責以下活動:
- 建立和管理請求和響應物件。
- 控制和管理 CherryPy 程序。
CherryPy – 配置
該框架自帶自己的配置系統,允許您引數化 HTTP 伺服器。配置設定可以儲存在語法接近 INI 格式的文字檔案中,也可以儲存在完整的 Python 字典中。
要配置 CherryPy 伺服器例項,開發人員需要使用設定的全域性部分。
global_conf = {
'global': {
'server.socket_host': 'localhost',
'server.socket_port': 8080,
},
}
application_conf = {
'/style.css': {
'tools.staticfile.on': True,
'tools.staticfile.filename': os.path.join(_curdir, 'style.css'),
}
}
This could be represented in a file like this:
[global]
server.socket_host = "localhost"
server.socket_port = 8080
[/style.css]
tools.staticfile.on = True
tools.staticfile.filename = "/full/path/to.style.css"
HTTP 相容性
CherryPy 一直在緩慢發展,但它包括 HTTP 規範的編譯,支援 HTTP/1.0,後來轉移到支援 HTTP/1.1。
據說 CherryPy 有條件地相容 HTTP/1.1,因為它實現了規範的所有必須和必需級別,但沒有實現所有應該級別。因此,CherryPy 支援以下 HTTP/1.1 功能 -
如果客戶端聲稱支援 HTTP/1.1,則必須在使用指定協議版本進行的任何請求中傳送報頭欄位。如果沒有這樣做,CherryPy 將立即停止處理請求。
CherryPy 生成一個 Date 報頭欄位,該欄位用於所有配置。
CherryPy 可以處理響應狀態程式碼 (100),並支援客戶端。
CherryPy 的內建 HTTP 伺服器支援持久連線,這是 HTTP/1.1 中的預設設定,透過使用 Connection: Keep-Alive 報頭。
CherryPy 正確處理分塊請求和響應。
CherryPy 以兩種不同的方式支援請求 - If-Modified-Since 和 If-Unmodified-Since 報頭,並根據請求相應地傳送響應。
CherryPy 允許任何 HTTP 方法。
CherryPy 處理客戶端和伺服器設定之間 HTTP 版本的組合。
多執行緒應用程式伺服器
CherryPy 基於多執行緒概念設計。每次開發人員在 CherryPy 名稱空間中獲取或設定值時,都是在多執行緒環境中完成的。
cherrypy.request 和 cherrypy.response 都是執行緒資料容器,這意味著您的應用程式可以獨立呼叫它們,並知道在執行時哪些請求透過它們進行代理。
使用執行緒模式的應用程式伺服器並不被高度重視,因為使用執行緒被認為會增加由於同步要求而導致問題的可能性。
其他替代方案包括 -
多程序模式
每個請求由其自己的 Python 程序處理。在這裡,伺服器的效能和穩定性可以被認為更好。
非同步模式
在這裡,接受新連線並將資料傳送回客戶端與請求程序非同步進行。此技術以其效率而聞名。
URL 分發
CherryPy 社群希望更加靈活,並且會感謝其他分發程式的解決方案。CherryPy 3 提供了其他內建分發程式,並提供了一種簡單的方法來編寫和使用您自己的分發程式。
- 用於開發 HTTP 方法的應用程式。(GET、POST、PUT 等)
- 定義 URL 中路由的那個 - 路由分發器
HTTP 方法分發器
在某些應用程式中,URI 與伺服器要對資源執行的操作無關。
例如,http://xyz.com/album/delete/10
URI 包含客戶端希望執行的操作。
預設情況下,CherryPy 分發器將以以下方式對映 -
album.delete(12)
上面提到的分發器已正確提及,但可以透過以下方式使其獨立 -
http://xyz.com/album/10
使用者可能想知道伺服器如何分發確切的頁面。此資訊由 HTTP 請求本身攜帶。當客戶端向伺服器發出請求時,CherryPy 會查詢最合適的處理程式,該處理程式是 URI 目標資源的表示。
DELETE /album/12 HTTP/1.1
路由分發器
以下是分發所需方法的引數列表 -
name 引數是連線路由的唯一名稱。
route 是用於匹配 URI 的模式。
controller 是包含頁面處理程式的例項。
使用路由分發器連線匹配 URI 的模式並關聯特定的頁面處理程式。
示例
讓我們舉一個例子來了解它是如何工作的 -
import random
import string
import cherrypy
class StringMaker(object):
@cherrypy.expose
def index(self):
return "Hello! How are you?"
@cherrypy.expose
def generate(self, length=9):
return ''.join(random.sample(string.hexdigits, int(length)))
if __name__ == '__main__':
cherrypy.quickstart(StringMaker ())
請按照以下步驟獲取上述程式碼的輸出 -
步驟 1 - 將上面提到的檔案儲存為tutRoutes.py。
步驟 2 - 訪問以下 URL -
https://:8080/generate?length=10
步驟 3 - 您將收到以下輸出 -
CherryPy - 工具箱
在 CherryPy 中,內建工具提供了一個呼叫 CherryPy 庫的單一介面。CherryPy 中定義的工具可以透過以下方式實現 -
- 從配置設定
- 作為 Python 裝飾器或透過頁面處理程式的特殊 _cp_config 屬性
- 作為可以從任何函式中應用的 Python 可呼叫物件
基本身份驗證工具
此工具的目的是為應用程式中設計的應用程式提供基本身份驗證。
引數
此工具使用以下引數 -
| 名稱 | 預設值 | 描述 |
|---|---|---|
| realm | N/A | 定義領域值的字串。 |
| users | N/A | 形式為使用者名稱:密碼的字典或返回此類字典的 Python 可呼叫函式。 |
| encrypt | None | Python 可呼叫物件,用於加密客戶端返回的密碼並將其與 users 字典中提供的加密密碼進行比較。 |
示例
讓我們舉一個例子來了解它是如何工作的 -
import sha
import cherrypy
class Root:
@cherrypy.expose
def index(self):
return """
<html>
<head></head>
<body>
<a href = "admin">Admin </a>
</body>
</html>
"""
class Admin:
@cherrypy.expose
def index(self):
return "This is a private area"
if __name__ == '__main__':
def get_users():
# 'test': 'test'
return {'test': 'b110ba61c4c0873d3101e10871082fbbfd3'}
def encrypt_pwd(token):
return sha.new(token).hexdigest()
conf = {'/admin': {'tools.basic_auth.on': True,
tools.basic_auth.realm': 'Website name',
'tools.basic_auth.users': get_users,
'tools.basic_auth.encrypt': encrypt_pwd}}
root = Root()
root.admin = Admin()
cherrypy.quickstart(root, '/', config=conf)
get_users 函式返回一個硬編碼字典,但也從資料庫或其他任何地方獲取值。admin 類包含此函式,該函式利用 CherryPy 的內建身份驗證工具。身份驗證加密密碼和使用者 ID。
基本身份驗證工具實際上並不安全,因為密碼可以被入侵者編碼和解碼。
快取工具
此工具的目的是提供 CherryPy 生成的內容的記憶體快取。
引數
此工具使用以下引數 -
| 名稱 | 預設值 | 描述 |
|---|---|---|
| invalid_methods | ("POST", "PUT", "DELETE") | 不應快取的 HTTP 方法的字串元組。這些方法還將使資源的任何快取副本失效(刪除)。 |
| cache_Class | MemoryCache | 要用於快取的類物件 |
解碼工具
此工具的目的是解碼傳入的請求引數。
引數
此工具使用以下引數 -
| 名稱 | 預設值 | 描述 |
|---|---|---|
| encoding | None | 它查詢 content-type 標頭 |
| Default_encoding | "UTF-8" | 未提供或找到時使用的預設編碼。 |
示例
讓我們舉一個例子來了解它是如何工作的 -
import cherrypy
from cherrypy import tools
class Root:
@cherrypy.expose
def index(self):
return """
<html>
<head></head>
<body>
<form action = "hello.html" method = "post">
<input type = "text" name = "name" value = "" />
<input type = ”submit” name = "submit"/>
</form>
</body>
</html>
"""
@cherrypy.expose
@tools.decode(encoding='ISO-88510-1')
def hello(self, name):
return "Hello %s" % (name, )
if __name__ == '__main__':
cherrypy.quickstart(Root(), '/')
上述程式碼從使用者那裡獲取一個字串,它會將使用者重定向到“hello.html”頁面,在那裡它將顯示為“Hello”,後面跟著給定的名稱。
上述程式碼的輸出如下 -
hello.html
CherryPy - 一個工作的應用程式
完整堆疊應用程式提供了一種透過某些命令或執行檔案來建立新應用程式的功能。
考慮像 web2py 框架這樣的 Python 應用程式;整個專案/應用程式都是根據 MVC 框架建立的。同樣,CherryPy 允許使用者根據自己的需求設定和配置程式碼的佈局。
在本章中,我們將詳細瞭解如何建立 CherryPy 應用程式並執行它。
檔案系統
應用程式的檔案系統在以下螢幕截圖中顯示 -
以下是我們檔案系統中各種檔案的簡要說明 -
config.py - 每個應用程式都需要一個配置檔案和一種載入它的方法。此功能可以在 config.py 中定義。
controllers.py - MVC 是使用者遵循的一種流行的設計模式。controllers.py 是所有將安裝在cherrypy.tree上的物件實現的地方。
models.py - 此檔案直接與資料庫互動以提供某些服務或儲存持久資料。
server.py - 此檔案與可與負載均衡代理一起正常工作的生產就緒 Web 伺服器互動。
Static - 它包含所有 CSS 和影像檔案。
Views - 它包含給定應用程式的所有模板檔案。
示例
讓我們詳細瞭解建立 CherryPy 應用程式的步驟。
步驟 1 - 建立一個應該包含應用程式的應用程式。
步驟 2 - 在目錄內,建立與專案對應的 Python 包。建立 gedit 目錄並在其中包含 _init_.py 檔案。
步驟 3 - 在包內,包含 controllers.py 檔案,內容如下 -
#!/usr/bin/env python
import cherrypy
class Root(object):
def __init__(self, data):
self.data = data
@cherrypy.expose
def index(self):
return 'Hi! Welcome to your application'
def main(filename):
data = {} # will be replaced with proper functionality later
# configuration file
cherrypy.config.update({
'tools.encode.on': True, 'tools.encode.encoding': 'utf-8',
'tools.decode.on': True,
'tools.trailing_slash.on': True,
'tools.staticdir.root': os.path.abspath(os.path.dirname(__file__)),
})
cherrypy.quickstart(Root(data), '/', {
'/media': {
'tools.staticdir.on': True,
'tools.staticdir.dir': 'static'
}
})
if __name__ == '__main__':
main(sys.argv[1])
步驟 4 - 考慮一個使用者透過表單輸入值的應用程式。讓我們在應用程式中包含兩個表單 - index.html 和 submit.html。
步驟 5 - 在上述 controllers 程式碼中,我們有index(),它是一個預設函式,如果呼叫特定的控制器,則首先載入。
步驟 6 - index() 方法的實現可以更改為以下方式 -
@cherrypy.expose
def index(self):
tmpl = loader.load('index.html')
return tmpl.generate(title='Sample').render('html', doctype='html')
步驟 7 - 這將在啟動給定應用程式時載入 index.html 並將其定向到給定的輸出流。index.html 檔案如下 -
index.html
<!DOCTYPE html >
<html>
<head>
<title>Sample</title>
</head>
<body class = "index">
<div id = "header">
<h1>Sample Application</h1>
</div>
<p>Welcome!</p>
<div id = "footer">
<hr>
</div>
</body>
</html>
步驟 8 - 如果要建立接受名稱和標題等值的表單,則必須在controller.py中的 Root 類中新增一個方法。
@cherrypy.expose
def submit(self, cancel = False, **value):
if cherrypy.request.method == 'POST':
if cancel:
raise cherrypy.HTTPRedirect('/') # to cancel the action
link = Link(**value)
self.data[link.id] = link
raise cherrypy.HTTPRedirect('/')
tmp = loader.load('submit.html')
streamValue = tmp.generate()
return streamValue.render('html', doctype='html')
步驟 9 - 要包含在 submit.html 中的程式碼如下 -
<!DOCTYPE html>
<head>
<title>Input the new link</title>
</head>
<body class = "submit">
<div id = " header">
<h1>Submit new link</h1>
</div>
<form action = "" method = "post">
<table summary = "">
<tr>
<th><label for = " username">Your name:</label></th>
<td><input type = " text" id = " username" name = " username" /></td>
</tr>
<tr>
<th><label for = " url">Link URL:</label></th>
<td><input type = " text" id=" url" name= " url" /></td>
</tr>
<tr>
<th><label for = " title">Title:</label></th>
<td><input type = " text" name = " title" /></td>
</tr>
<tr>
<td></td>
<td>
<input type = " submit" value = " Submit" />
<input type = " submit" name = " cancel" value = "Cancel" />
</td>
</tr>
</table>
</form>
<div id = "footer">
</div>
</body>
</html>
步驟 10 - 您將收到以下輸出 -
這裡,方法名稱定義為“POST”。始終務必交叉驗證檔案中指定的方法。如果方法包含“POST”方法,則應在資料庫中的相應欄位中重新檢查值。
如果方法包含“GET”方法,則要儲存的值將顯示在 URL 中。
CherryPy - Web服務
Web 服務是一組基於 Web 的元件,有助於在應用程式或系統之間交換資料,其中還包括開放協議和標準。它可以在 Web 上釋出、使用和查詢。
Web 服務有多種型別,例如 RWS(RESTfUL Web 服務)、WSDL、SOAP 等。
REST - 表現層狀態轉移
一種遠端訪問協議,它將狀態從客戶端傳輸到伺服器,可用於操縱狀態而不是呼叫遠端過程。
沒有定義任何特定的編碼或結構以及返回有用錯誤訊息的方式。
使用 HTTP“動詞”執行狀態轉移操作。
資源使用 URL 唯一標識。
它不是 API,而是 API 傳輸層。
REST 保持網路上資源的命名法,並提供統一的機制來對這些資源執行操作。每個資源至少由一個識別符號標識。如果 REST 基礎設施是使用 HTTP 為基礎實現的,則這些識別符號被稱為統一資源識別符號 (URI)。
以下是 URI 集的兩個常見子集 -
| 子集 | 全稱 | 示例 |
|---|---|---|
| URL | 統一資源定位符 | http://www.gmail.com/ |
| URN | 統一資源名稱 | urn:isbn:0-201-71088-9 urn:uuid:13e8cf26-2a25-11db-8693-000ae4ea7d46 |
在瞭解 CherryPy 架構的實現之前,讓我們先關注 CherryPy 的架構。
CherryPy 包括以下三個元件 -
cherrypy.engine - 它控制程序啟動/終止和事件處理。
cherrypy.server - 它配置和控制 WSGI 或 HTTP 伺服器。
cherrypy.tools - 一套與處理 HTTP 請求正交的實用程式工具箱。
透過 CherryPy 的 REST 介面
RESTful Web 服務藉助以下內容實現了 CherryPy 架構的每個部分 -
- 身份驗證
- 授權
- 結構
- 封裝
- 錯誤處理
身份驗證
身份驗證有助於驗證我們正在與之互動的使用者。CherryPy 包括處理每種身份驗證方法的工具。
def authenticate():
if not hasattr(cherrypy.request, 'user') or cherrypy.request.user is None:
# < Do stuff to look up your users >
cherrypy.request.authorized = False # This only authenticates.
Authz must be handled separately.
cherrypy.request.unauthorized_reasons = []
cherrypy.request.authorization_queries = []
cherrypy.tools.authenticate = \
cherrypy.Tool('before_handler', authenticate, priority=10)
上述函式 authenticate() 將有助於驗證客戶端或使用者的是否存在。內建工具有助於以系統的方式完成此過程。
授權
授權透過 URI 幫助維護流程的完整性。此過程還有助於透過使用者令牌線索來轉換物件。
def authorize_all():
cherrypy.request.authorized = 'authorize_all'
cherrypy.tools.authorize_all = cherrypy.Tool('before_handler', authorize_all, priority=11)
def is_authorized():
if not cherrypy.request.authorized:
raise cherrypy.HTTPError("403 Forbidden",
','.join(cherrypy.request.unauthorized_reasons))
cherrypy.tools.is_authorized = cherrypy.Tool('before_handler', is_authorized,
priority = 49)
cherrypy.config.update({
'tools.is_authorized.on': True,
'tools.authorize_all.on': True
})
授權的內建工具有助於以系統的方式處理例程,如前面的示例中所述。
結構
維護 API 的結構有助於減少對映應用程式 URI 的工作量。始終有必要保持 API 可發現且簡潔。CherryPy 框架的 API 基本結構應包含以下內容:
- 賬戶和使用者
- 自動回覆器
- 聯絡人
- 檔案
- 資料夾
- 列表和欄位
- 訊息和批處理
封裝
封裝有助於建立輕量級、人類可讀且可供各種客戶端訪問的 API。包含建立、檢索、更新和刪除在內的專案列表需要 API 的封裝。
錯誤處理
如果 API 在特定時刻無法執行,則此過程會管理任何錯誤。例如,400 表示錯誤請求,403 表示未授權請求。
示例
請考慮以下資料庫、驗證或應用程式錯誤示例。
import cherrypy
import json
def error_page_default(status, message, traceback, version):
ret = {
'status': status,
'version': version,
'message': [message],
'traceback': traceback
}
return json.dumps(ret)
class Root:
_cp_config = {'error_page.default': error_page_default}
@cherrypy.expose
def index(self):
raise cherrypy.HTTPError(500, "Internal Sever Error")
cherrypy.quickstart(Root())
以上程式碼將產生以下輸出:
由於 CherryPy 具有內建的訪問工具,因此可以輕鬆管理 API(應用程式程式設計介面)。
HTTP 方法
在資源上操作的 HTTP 方法列表如下:
| 序號 | HTTP 方法和操作 |
|---|---|
| 1. | HEAD 檢索資源元資料。 |
| 2. | GET 檢索資源元資料和內容。 |
| 3. | POST 請求伺服器使用請求正文中包含的資料建立新資源。 |
| 4. | PUT 請求伺服器用請求正文中包含的資源替換現有資源。 |
| 5. | DELETE 請求伺服器刪除由該 URI 標識的資源。 |
| 6. | OPTIONS 請求伺服器返回有關功能的詳細資訊,無論是全域性的還是針對特定資源的。 |
Atom 釋出協議 (APP)
APP 誕生於 Atom 社群,作為 HTTP 之上的應用程式級協議,允許釋出和編輯 Web 資源。APP 伺服器和客戶端之間訊息的單位基於 Atom XML 文件格式。
Atom 釋出協議定義了一組 APP 服務和使用者代理之間使用 HTTP 及其機制的操作,以及 Atom XML 文件格式作為訊息單位。
APP 首先定義服務文件,該文件為使用者代理提供 APP 服務提供的不同集合的 URI。
示例
讓我們舉一個例子來演示 APP 如何工作:
<?xml version = "1.0" encoding = "UTF-8"?>
<service xmlns = "http://purl.org/atom/app#" xmlns:atom = "http://www.w3.org/2005/Atom">
<workspace>
<collection href = "http://host/service/atompub/album/">
<atom:title> Albums</atom:title>
<categories fixed = "yes">
<atom:category term = "friends" />
</categories>
</collection>
<collection href = "http://host/service/atompub/film/">
<atom:title>Films</atom:title>
<accept>image/png,image/jpeg</accept>
</collection>
</workspace>
</service>
APP 指定了如何使用 HTTP 方法對集合成員或集合本身執行基本的 CRUD 操作,如下表所述:
| 操作 | HTTP 方法 | 狀態碼 | 內容 |
|---|---|---|---|
| 檢索 | GET | 200 | 表示資源的 Atom 條目 |
| 建立 | POST | 201 | 透過 Location 和 Content-Location 標頭獲取新建立資源的 URI |
| 更新 | PUT | 200 | 表示資源的 Atom 條目 |
| 刪除 | DELETE | 200 | None |
CherryPy - 表示層
表示層確保透過它的通訊針對預期的接收者。CherryPy 透過各種模板引擎維護表示層的執行。
模板引擎在業務邏輯的幫助下獲取頁面的輸入,然後將其處理到最終頁面,該頁面僅針對目標受眾。
Kid — 模板引擎
Kid 是一款簡單的模板引擎,其中包含要處理的模板名稱(這是必須的)以及在呈現模板時要傳遞的資料輸入。
首次建立模板時,Kid 會建立一個 Python 模組,該模組可以作為模板的快取版本提供服務。
kid.Template 函式返回模板類的例項,該例項可用於呈現輸出內容。
模板類提供以下命令集:
| 序號 | 命令和描述 |
|---|---|
| 1. | serialize 它將輸出內容作為字串返回。 |
| 2. | generate 它將輸出內容作為迭代器返回。 |
| 3. | write 它將輸出內容轉儲到檔案物件中。 |
這些命令使用的引數如下:
| 序號 | 命令和描述 |
|---|---|
| 1. | encoding 它通知如何編碼輸出內容 |
| 2. | fragment 它是一個布林值,指示是否使用 XML 前言或文件型別宣告 |
| 3. | output 此型別的序列化用於呈現內容 |
示例
讓我們舉一個例子來理解 kid 如何工作:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns:py = "http://purl.org/kid/ns#">
<head>
<title>${title}</title>
<link rel = "stylesheet" href = "style.css" />
</head>
<body>
<p>${message}</p>
</body>
</html>
The next step after saving the file is to process the template via the Kid engine.
import kid
params = {'title': 'Hello world!!', 'message': 'CherryPy.'}
t = kid.Template('helloworld.kid', **params)
print t.serialize(output='html')
Kid 的屬性
以下是 Kid 的屬性:
基於 XML 的模板語言
它是一種基於 XML 的語言。Kid 模板必須是格式良好的 XML 文件,並具有正確的命名約定。
Kid 在 XML 元素中實現了屬性,以更新底層引擎以執行到達該元素的操作。為了避免與 XML 文件中其他現有屬性重疊,Kid 引入了自己的名稱空間。
<p py:if = "...">...</p>
變數替換
Kid 帶有一個變數替換方案和一個簡單的方法 — ${variable-name}。
變數既可以用於元素的屬性中,也可以用作元素的文字內容。Kid 將在每次執行時評估變數。
如果使用者需要 ${something} 作為字面字串的輸出,可以透過將美元符號加倍來使用變數替換對其進行轉義。
條件語句
要切換模板中的不同情況,請使用以下語法:
<tag py:if = "expression">...</tag>
這裡,tag 是元素的名稱,例如 DIV 或 SPAN。
表示式是 Python 表示式。如果作為布林值,它計算結果為 True,則元素將包含在輸出內容中,否則它將不是輸出內容的一部分。
迴圈機制
要在 Kid 中迴圈元素,請使用以下語法:
<tag py:for = "expression">...</tag>
這裡,tag 是元素的名稱。表示式是 Python 表示式,例如 for value in [...].
示例
以下程式碼顯示了迴圈機制的工作原理:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>${title}</title>
<link rel = "stylesheet" href = "style.css" />
</head>
<body>
<table>
<caption>A few songs</caption>
<tr>
<th>Artist</th>
<th>Album</th>
<th>Title</th>
</tr>
<tr py:for = "info in infos">
<td>${info['artist']}</td>
<td>${info['album']}</td>
<td>${info['song']}</td>
</tr>
</table>
</body>
</html>
import kid
params = discography.retrieve_songs()
t = kid.Template('songs.kid', **params)
print t.serialize(output='html')
使用迴圈機制的上述程式碼的輸出如下:
CherryPy - Ajax的使用
到 2005 年,所有 Web 應用程式都遵循的模式是每個頁面管理一個 HTTP 請求。從一個頁面導航到另一個頁面需要載入整個頁面。這將降低效能水平。
因此,出現了富客戶端應用程式,它們通常會嵌入 AJAX、XML 和 JSON。
AJAX
非同步 JavaScript 和 XML (AJAX) 是一種建立快速且動態網頁的技術。AJAX 允許網頁透過在幕後與伺服器交換少量資料來非同步更新。這意味著可以更新網頁的部分內容,而無需重新載入整個頁面。
Google 地圖、Gmail、YouTube 和 Facebook 是 AJAX 應用程式的一些示例。
Ajax 基於使用 JavaScript 傳送 HTTP 請求的想法;更具體地說,AJAX 依賴於 XMLHttpRequest 物件及其 API 來執行這些操作。
JSON
JSON 是一種以 JavaScript 應用程式可以評估它們並將它們轉換為以後可以操作的 JavaScript 物件的方式來攜帶序列化 JavaScript 物件的方法。
例如,當用戶請求伺服器以 JSON 格式格式化的專輯物件時,伺服器將返回以下輸出:
{'description': 'This is a simple demo album for you to test', 'author': ‘xyz’}
現在資料是一個 JavaScript 關聯陣列,並且可以透過以下方式訪問 description 欄位:
data ['description'];
將 AJAX 應用於應用程式
考慮包含名為“media”的資料夾的應用程式,其中包含 index.html 和 Jquery 外掛,以及包含 AJAX 實現的檔案。讓我們考慮將檔案命名為“ajax_app.py”
ajax_app.py
import cherrypy
import webbrowser
import os
import simplejson
import sys
MEDIA_DIR = os.path.join(os.path.abspath("."), u"media")
class AjaxApp(object):
@cherrypy.expose
def index(self):
return open(os.path.join(MEDIA_DIR, u'index.html'))
@cherrypy.expose
def submit(self, name):
cherrypy.response.headers['Content-Type'] = 'application/json'
return simplejson.dumps(dict(title="Hello, %s" % name))
config = {'/media':
{'tools.staticdir.on': True,
'tools.staticdir.dir': MEDIA_DIR,}
}
def open_page():
webbrowser.open("http://127.0.0.1:8080/")
cherrypy.engine.subscribe('start', open_page)
cherrypy.tree.mount(AjaxApp(), '/', config=config)
cherrypy.engine.start()
類“AjaxApp”重定向到“index.html”的網頁,該網頁包含在 media 資料夾中。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
" http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns = "http://www.w3.org/1999/xhtml" lang = "en" xml:lang = "en">
<head>
<title>AJAX with jQuery and cherrypy</title>
<meta http-equiv = " Content-Type" content = " text/html; charset=utf-8" />
<script type = " text/javascript" src = " /media/jquery-1.4.2.min.js"></script>
<script type = " text/javascript">
$(function() {
// When the testform is submitted...
$("#formtest").submit(function() {
// post the form values via AJAX...
$.post('/submit', {name: $("#name").val()}, function(data) {
// and set the title with the result
$("#title").html(data['title']) ;
});
return false ;
});
});
</script>
</head>
<body>
<h1 id = "title">What's your name?</h1>
<form id = " formtest" action = " #" method = " post">
<p>
<label for = " name">Name:</label>
<input type = " text" id = "name" /> <br />
<input type = " submit" value = " Set" />
</p>
</form>
</body>
</html>
AJAX 函式包含在 <script> 標記中。
輸出
以上程式碼將產生以下輸出:
使用者提交值後,將實現 AJAX 功能,螢幕將重定向到如下所示的表單:
CherryPy - 演示應用程式
在本章中,我們將重點介紹如何在 CherryPy 框架中建立應用程式。
考慮Photoblog 應用程式作為 CherryPy 的演示應用程式。Photoblog 應用程式是一個普通的部落格,但主要文字將是照片而不是文字。Photoblog 應用程式的主要重點是開發人員可以更多地關注設計和實現。
基本結構 - 實體設計
實體設計應用程式的基本結構。以下是 Photoblog 應用程式的實體:
- 膠捲
- 照片
- 相簿
以下是實體關係的基本類圖:
設計結構
如上一章所述,專案的結構設計將如以下螢幕截圖所示:
考慮給定的應用程式,該應用程式具有 Photoblog 應用程式的子目錄。子目錄是 Photo、Album 和 Film,其中將包含 controllers.py、models.py 和 server.py。
在功能上,Photoblog 應用程式將提供 API 以透過傳統的 CRUD 介面(建立、檢索、更新和刪除)來操作這些實體。
連線到資料庫
儲存模組包含一組操作;其中之一是與資料庫連線。
由於它是一個完整的應用程式,因此與資料庫的連線對於 API 和維護建立、檢索、更新和刪除的功能是強制性的。
import dejavu
arena = dejavu.Arena()
from model import Album, Film, Photo
def connect():
conf = {'Connect': "host=localhost dbname=Photoblog user=test password=test"}
arena.add_store("main", "postgres", conf)
arena.register_all(globals())
上面程式碼中的 arena 將是我們底層儲存管理器和業務邏輯層之間的介面。
connect 函式為 PostgreSQL RDBMS 的 arena 物件添加了一個儲存管理器。
獲取連線後,我們可以根據業務需求建立表單並完成應用程式的執行。
在建立任何應用程式之前,最重要的事情是實體對映和設計應用程式的結構。
CherryPy - 測試
測試是一個過程,在此過程中,應用程式將從不同的角度進行,以便於:
- 查詢問題列表
- 查詢預期結果與實際結果、輸出、狀態等之間的差異。
- 瞭解實施階段。
- 查詢對實際用途有用的應用程式。
測試的目的不是指責開發人員,而是提供工具並提高質量,以評估應用程式在給定時間的狀態。
測試需要提前計劃。這需要定義測試目的,瞭解測試用例的範圍,列出業務需求並瞭解專案不同階段中涉及的風險。
測試定義為在一系列系統或應用程式上需要驗證的方面。以下是常見測試方法的列表:
單元測試 - 通常由開發人員自己執行。目的是檢查程式碼單元是否按預期工作。
可用性測試 - 開發人員通常會忘記他們正在為終端使用者編寫應用程式,而終端使用者並不瞭解系統。可用性測試驗證產品的優缺點。
功能/驗收測試 - 在可用性測試檢查應用程式或系統是否可用時,功能測試確保每個指定的特性都已實現。
負載和效能測試 - 執行此操作是為了瞭解系統是否可以適應要進行的負載和效能測試。這可能導致硬體發生變化、最佳化 SQL 查詢等。
迴歸測試 - 驗證產品的後續版本不會破壞任何以前的特性。
可靠性和彈性測試 - 可靠性測試有助於驗證系統應用程式在一個或多個元件發生故障時的狀態。
單元測試
照片部落格應用程式不斷使用單元測試來檢查以下內容:
- 新功能正常工作並按預期工作。
- 新程式碼釋出不會破壞現有功能。
- 缺陷已修復並保持修復狀態。
Python 帶有一個標準的 unittest 模組,提供了一種不同的單元測試方法。
單元測試
unittest 源自 JUnit,JUnit 是由 Kent Beck 和 Erich Gamma 開發的 Java 單元測試包。單元測試簡單地返回定義的資料。可以定義模擬物件。這些物件允許針對我們設計的介面進行測試,而無需依賴整個應用程式。它們還提供了一種在隔離模式下執行測試的方法,其中包括其他測試。
讓我們以如下方式定義一個虛擬類:
import unittest class DummyTest(unittest.TestCase): def test_01_forward(self): dummy = Dummy(right_boundary=3) self.assertEqual(dummy.forward(), 1) self.assertEqual(dummy.forward(), 2) self.assertEqual(dummy.forward(), 3) self.assertRaises(ValueError, dummy.forward) def test_02_backward(self): dummy = Dummy(left_boundary=-3, allow_negative=True) self.assertEqual(dummy.backward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.backward(), -3) self.assertRaises(ValueError, dummy.backward) def test_03_boundaries(self): dummy = Dummy(right_boundary=3, left_boundary=-3,allow_negative=True) self.assertEqual(dummy.backward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.forward(), -1) self.assertEqual(dummy.backward(), -2) self.assertEqual(dummy.backward(), -3)
程式碼的解釋如下:
應匯入 unittest 模組以提供給定類的單元測試功能。
應透過子類化 unittest 建立一個類。
上面程式碼中的每個方法都以單詞 test 開頭。所有這些方法都由 unittest 處理程式呼叫。
測試用例呼叫 assert/fail 方法來管理異常。
將此視為執行測試用例的示例:
if __name__ == '__main__': unittest.main()
執行測試用例的結果(輸出)如下:
---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
功能測試
一旦應用程式功能根據需求開始成形,一組功能測試就可以驗證應用程式在規範方面的正確性。但是,測試應自動化以獲得更好的效能,這將需要使用 Selenium 等第三方產品。
CherryPy 提供了類似內建函式的輔助類,以簡化功能測試的編寫。
負載測試
根據您正在編寫的應用程式以及您在容量方面的期望,您可能需要執行負載和效能測試,以檢測應用程式中可能存在的瓶頸,這些瓶頸阻止它達到一定的效能水平。
本節將不詳細介紹如何進行效能或負載測試,因為它超出了 FunkLoad 包的範圍。
FunkLoad 的一個非常基本的示例如下:
from funkload.FunkLoadTestCase
import FunkLoadTestCase
class LoadHomePage(FunkLoadTestCase):
def test_homepage(self):
server_url = self.conf_get('main', 'url')
nb_time = self.conf_getInt('test_homepage', 'nb_time')
home_page = "%s/" % server_url
for i in range(nb_time):
self.logd('Try %i' % i)
self.get(home_page, description='Get gome page')
if __name__ in ('main', '__main__'):
import unittest
unittest.main()
以下是上述程式碼的詳細解釋:
測試用例必須繼承自 FunkLoadTestCase 類,以便 FunkLoad 可以完成其在測試期間跟蹤發生情況的內部工作。
類名很重要,因為 FunkLoad 將根據類名查詢檔案。
設計的測試用例可以直接訪問配置檔案。Get() 和 post() 方法只是針對伺服器呼叫以獲取響應。
CherryPy - 應用程式部署
本章將更多地關注基於 CherryPy 的應用程式,該應用程式透過內建的 CherryPy HTTP 伺服器啟用 SSL。
配置
Web 應用程式需要不同級別的配置設定:
Web 伺服器 - 與 HTTP 伺服器相關的設定
引擎 - 與引擎託管相關的設定
應用程式 - 使用者使用的應用程式
部署
CherryPy 應用程式的部署被認為是一種非常簡單的方法,其中所有必需的軟體包都可從 Python 系統路徑獲得。在共享的 Web 託管環境中,Web 伺服器將駐留在前端,這允許主機提供商執行過濾操作。前端伺服器可以是 Apache 或lighttpd。
本節將介紹一些在 Apache 和 lighttpd Web 伺服器後面執行 CherryPy 應用程式的解決方案。
cherrypy
def setup_app():
class Root:
@cherrypy.expose
def index(self):
# Return the hostname used by CherryPy and the remote
# caller IP address
return "Hello there %s from IP: %s " %
(cherrypy.request.base, cherrypy.request.remote.ip)
cherrypy.config.update({'server.socket_port': 9091,
'environment': 'production',
'log.screen': False,
'show_tracebacks': False})
cherrypy.tree.mount(Root())
if __name__ == '__main__':
setup_app()
cherrypy.server.quickstart()
cherrypy.engine.start()
SSL
SSL(安全套接字層)可以在基於 CherryPy 的應用程式中獲得支援。要啟用 SSL 支援,必須滿足以下要求:
- 在使用者的環境中安裝 PyOpenSSL 軟體包
- 在伺服器上擁有 SSL 證書和私鑰
建立證書和私鑰
讓我們處理證書和私鑰的要求:
- 首先,使用者需要一個私鑰:
openssl genrsa -out server.key 2048
- 此金鑰不受密碼保護,因此保護較弱。
- 將發出以下命令:
openssl genrsa -des3 -out server.key 2048
程式將要求輸入密碼。如果您的 OpenSSL 版本允許您提供空字串,請這樣做。否則,輸入預設密碼,然後從生成的金鑰中將其刪除,如下所示:
openssl rsa -in server.key -out server.key
- 證書的建立如下:
openssl req -new -key server.key -out server.csr
此過程將要求您輸入一些詳細資訊。為此,必須發出以下命令:
openssl x509 -req -days 60 -in server.csr -signkey server.key -out server.crt
新簽名的證書有效期為 60 天。
以下程式碼顯示了其實現:
import cherrypy
import os, os.path
localDir = os.path.abspath(os.path.dirname(__file__))
CA = os.path.join(localDir, 'server.crt')
KEY = os.path.join(localDir, 'server.key')
def setup_server():
class Root:
@cherrypy.expose
def index(self):
return "Hello there!"
cherrypy.tree.mount(Root())
if __name__ == '__main__':
setup_server()
cherrypy.config.update({'server.socket_port': 8443,
'environment': 'production',
'log.screen': True,
'server.ssl_certificate': CA,
'server.ssl_private_key': KEY})
cherrypy.server.quickstart()
cherrypy.engine.start()
下一步是啟動伺服器;如果成功,您將在螢幕上看到以下訊息:
HTTP Serving HTTPS on https://:8443/