Bottle 是一個很是小巧但高效的微型 Python Web 框架,它被設計爲僅僅只有一個文件的Python模塊,而且除Python標準庫外,它不依賴於任何第三方模塊。前端
- 路由(Routing):將請求映射到函數,能夠建立十分優雅的 URL
- 模板(Templates):Pythonic 而且快速的 Python 內置模板引擎,同時還支持 mako, jinja2, cheetah 等第三方模板引擎
- 工具集(Utilites):快速的讀取 form 數據,上傳文件,訪問 cookies,headers 或者其它 HTTP 相關的 metadata
- 服務器(Server):內置HTTP開發服務器,而且支持 paste, fapws3, bjoern, Google App Engine, Cherrypy 或者其它任何 WSGI HTTP 服務器
安裝 Bottle
正如上面所說的, Bottle 被設計爲僅僅只有一個文件,咱們甚至能夠不安裝它,直接將 bottle.py 文件下載並複製到咱們的應用中就可使用了,這是一個好辦法,可是若是仍是想將其安裝,那麼咱們能夠像安裝其它的 Python 模塊同樣:python
sudo easy_install -U bottle
若是咱們直接將 bottle.py 下載到本身的應用中的話,咱們能夠創建下面這樣的目錄結構:web
+ application +----bottle.py +----app.py
咱們能夠將下面的建立 Bottle 實例的示例代碼複製到 app.py 文件中,運行該文件便可。ajax
示例:Bottle 的 「Hello World」 程序
下面的代碼咱們建立了一個十分簡單可是完整的 Bottle 應用程序(在Python Consle)中:sql
>>> from bottle import route, run >>> @route('/hello/:name') ... def index(name = 'World'): ... return '<strong>Hello {}!'.format(name) ... >>> run(host='localhost',port=8080) Bottle server starting up (using WSGIRefServer())... Listening on http://localhost:8080/ Use Ctrl-C to quit.
在 Python Consle中輸入上面的代碼,咱們就獲得了一個最簡單但完整的 Web 應用,訪問:「http://localhost:8080/hello/bottle」試試。上面到底發生了什麼?數據庫
- 首先,咱們導入了兩個 Bottle 的組件,
route()
Decorator 和run()
函數 route()
能夠將一個函數與一個URL進行綁定,在上面的示例中,route 將 「/hello/:name’ 這個URL地址綁定到了 「index(name = ‘World’)」 這個函數上- 這個是一個關聯到 「/hello」 的 handler function 或者 callback ,任何對 「/hello」 這個URL的請求都將被遞交到這個函數中
- 咱們得到請求後,index() 函數返回簡單的字符串
- 最後,run() 函數啓動服務器,而且咱們設置它在 「localhost」 和 8080 端口上運行
上面這種方法僅僅只是展現一下下 Bottle 的簡單,咱們還能夠像下面這樣,建立一個 Bottle 對象 app,而後咱們會將全部的函數都映射到 app 的 URL 地址上,如上示例咱們能夠用下面這種辦法來實現:json
from bottle import Bottle, run app = Bottle() @app.route('/hello') def hello(): return "Hello World!" run(app, host='localhost', port=8080)
Bottle 的這種 URL 地址映射方法與我一直使用的 Flask 的地址映射方法很類似,到如今爲止,我彷佛只看到它們只是語法上面有些話不一樣。flask
路由器(Request Routing)
Bottle 應用會有一個 URL 路由器,它將 URL 請求地址綁定到回調函數上,每請求一些 URL,其對應的 回調函數就會運行一些,而回調函數返回值將被髮送到瀏覽器,你能夠在你的應用中經過 route()
函數添加不限數目的路由器。瀏覽器
from bottle import route @route('/') @route('/index.html') def index(): return '<a href="/hello">Go to Hello World Page</a>' @route('/hello') def hello(): return 'Hello World'
就像你看到,你所發出的訪問請求(URL),應用並無返回服務器上真實的文件,而是返回與該URL綁定的函數的返回值,若是其一個URL沒有被綁定到任何回調函數上,那麼 Bottle 將返回「404 Page Not Found」 的錯誤頁面
動態路由(Dynamic Routes)
Bottle 有本身特有的 URL 語法,這讓咱們能夠很輕鬆的在 URL 地址中加入通配符,這樣,一個 route 將能夠映射到無數的 URL 上,這些動態的 路由經常被用來建立一些有規律性的內容頁面的地址,好比博客文章地址「/archive/1234.html」 或者 「/wiki/Page_Title」,這在上面的示例我已經演示過了,還記得嗎?
@route(’/hello/:name’)
def hello(name = ‘World’): return ‘Hello {}!’.format(name)
上面的路由器,可讓咱們經過「/hello/costony」或者「/hello/huwenxiao」等地址來訪問,而 Bottle 返回的內容將是「Hello costony!」 或者 「Hello huwenxiao!」,「/hello/」以後的字符串交被返回來,默認的通配符將匹配全部下一個「/」出現以前的字符。咱們還能夠對通配符進行格式化:
@route('/object/:id#[0-9]+#') def view_object(id): return 'Object ID: {}'.format(id)
上面的路由將只容許 id 爲由數字「0-9」組成的數字,而其它的字符串都將返回 404 錯誤。
HTTP 請求方法(Request Methods)
HTTP 協議爲不一樣的需求定義了許多不一樣的請求方法,在 Bottle 中,GET方法將是全部未指明請求訪問的路由會默認使用的方法,這些未指明方法的路由都將只接收 GET 請求,要處理如 POST, PUT 或者 DELETE 等等的其它請求,你必須主動地在 route()
函數中添加method
關鍵字,或者使用下面這些 decorators:@get()@ , post()
, put()
, delete()
。
POST 方法在常常被用來處理 HTML 的表單數據,下面的示例演示了一個 登錄表單的處理過程:
from bottle import get, post, request #@route('/login') @get('/login') def login_form(): return '''<form method = "POST"> <input name="name" type="text" /> <input name="password" type="password" /> <input type="submit" value="Login" /> </form>''' #@route('/login', method = 'POST') @post('/login') def login(): name = request.forms.get('name') password = request.forms.get('password') if check_login(name, password): return '<p>Your login was correct</p>' else: return '<p>Login failed</p>'
在上面的示例中,@/login@ 被綁定到兩個不一樣的回調函數上,一個處理 GET 請求,另外一個處理 POST 請求,第一個返咱們的登錄表單,第二個接收登錄表單提交的數據,並進行處理,獲得結果後,返回結果。
自動回退(Automatic Fallbacks)
特殊的 HEAD 方法,常常被用來處理一些僅僅只須要返回請求元信息而不須要返回整個請求結果的事務,這些HEAD方法十分有用,可讓咱們僅僅只得到咱們須要的數據,而沒必要要返回整個文檔,Bottle 能夠幫助咱們很簡單的實現這些功能,它會將這些請求映射到與URL綁定的回調函數中,而後自動截取請求須要的數據,這樣一來,你再也不須要定義任何特殊的 HEAD 路由了。
靜態文件路由(Routing Static Files)
對於靜態文件, Bottle 內置的服務器並不會自動的進行處理,這須要你本身定義一個路由,告訴服務器在哪些文件是須要服務的,而且在哪裏能夠找到它們,咱們能夠寫以下面這樣的一個路由器:
from bottle import static_file @route('/static/:filename') def server_static(filename): return static_file(filename, root='/path/to/your/static/files')
static_file()
函數是一個安全且方便的用來返回靜態文件請求的函數,上面的示例中,咱們只返回」/path/to/your/static/files」 路徑下的文件,由於 :filename 通配符並不接受任何 「/」 的字符,若是咱們想要「/path/to/your/static/files」 目錄的子目錄下的文件也被處理,那麼咱們可使用一個格式化的通配符:
@route('/static/:path#.+#') def server_static(path): return static_file(path, root='/path/to/your/static/files')
錯誤頁面(Error Pages)
若是任何請求的URL沒有的到匹配的回調函數,那麼 Bottle 都會返回錯誤頁面,你可使用 error()
decorator 來抓取 HTTP 狀態,並設置本身的相關回調函數,好比下面咱們的處理404錯誤的函數:
@error(404) def error404(error): return '404 error, nothing here, sorry!'
這個時候,404 文件未找到錯誤將被上面的自定義404錯誤處理方法代替,傳送給錯誤處理函數的惟一的一個參數是一個 HTTPError
實例,它很是將普通的 request,因此,你也能夠有 request 中讀取到,也能夠寫入 response 中,而且返回任何 HTTPError 支持的數據類型。
生成內容(Generating Content)
在純粹的 WSGI中,你的應用能返回的數據類型是十分有限的,你必須返回可迭代的字符串,你能返回字符串是由於字符串是能夠迭代的,可是這致使服務器將你的內容按一字符一字符的傳送,這個時候,Unicode 字符將不容許被返回了,這是確定不行的。
Bottle 則支持了更多的數據類型,它甚至添加了一個 Content-Length
頭信息,而且自動編碼 Unicode 數據,下面列舉了 Bottle 應用中,你能夠返回的數據類型,而且簡單的介紹了一下這些數據類型的數據都是怎麼被 Bottle 處理的:
數據類型 | 介紹 |
---|---|
字典(Dictionaries) | Python 內置的字典類型數據將自動被轉換爲 JSON 字符串,而且添加 Content-Type 爲 ’application/json’ 的頭信息返回至瀏覽器,這讓咱們能夠很方便的創建基於 JSON 的API |
空字符串,False,None或者任何非真的數據 | Bottle 將爲這類數據建立 ContentLength 頭文件,被設置爲 0 返回至瀏覽器 |
Unicode 字符串 | Unicode 字符串將自動的按 Content-Type 頭文件中定義的編碼格式進行編碼(默認爲UTF8),接着按普通的字符串進行處理 |
字節串(Byte strings) | Bottle 返回整個字符串(而不是按字節一個一個返回),同時增長 Content-Length 頭文件標示字節串長度 |
HTTPError 與HTTPResponse 實例 |
返回這些實例就像拋出異常同樣,對於 HTTPError,錯誤將被與相關函數處理 |
文件對象 | 而後具備 .read() 方法的對象都被看做文件或者相似文件的對象進行處理,並傳送給 WSGI 服務器框架定義wsgi.file_wrapper 回調函數,某一些WSGI服務器會使用系統優化的請求方式(Sendfile)來發送文件。 |
迭代器與生成品 | 你能夠在你的回調函數使用 yield 或者 返回一個迭代器,只要yield的對象是字符串,Unicode 字符串,HTTPError 或者 HTTPResponse 對象就行,可是不容許使用嵌套的迭代器,須要注意的是,當 yield 的值第一次爲非空是, HTTP 的狀態 和 頭文件將被髮送到 瀏覽器 |
若是你返回一個 str
類子類的實例,而且帶有 read()
方法,那它仍是將按 字符串進行處理,由於字符串有更高一級的優先處理權。
改變默認編碼
Bottle 依照 Content-Type 頭文件中 charset
參數來對字符串進行編碼,該頭文件默認爲 text/html; charset=UTF8
,而且能夠被Response.content_type
屬性修改,或者直接被 Response.charset
屬性修改:
from bottle import response @route('/iso') def get_iso(): response.charset = 'ISO-8859-15' return u'This will be sent with ISO-8859-15 encoding.' @route('/latin9') def get_latin(): response.content_type = 'text/html; charset=latin9' return u'ISO-8859-15 is also known as latin9.'
因爲某些罕見的緣由,Python 編碼的名稱可能與 HTTP 編碼的名稱不一致,這時你須要作兩方法的工做首先設置Response.content_type
頭文件,而後還須要設置 Response.charset
。
靜態文件
你能夠直接返回文件,可是 Bottle 推薦使用 static_file()
方法,它會自動的猜想文件的 mime-type,追加 Last-Modified
頭文件,徹底的自定義須要服務的文件路徑,而且能處理錯誤(好比 404),而且它還支持 If-Modified-Since
頭文件而且能夠返回 304 Not Modified
響應,你還可使用一個自定義的 mime-type 來重寫 mime-type 猜想的值。
from bottle import static_file @route('/images/:filename#.*\.png#') def send_image(filename): return static_file(filename, root='/path/to/image/files', mimetype = 'image/png') @route('/static/:filename') def send_static(filename): return static_file(filename, root='/path/to/static/files')
若是你真的須要,你還能夠以異常的形式拋出文件。
強制下載
絕大多數瀏覽器在知道下載的文件的MIME類型而且該文件類型被綁定到某一個應用程序時(好比PDF文件),它們都會自動的打開該文件,若是你不想這樣,你能夠強制的要求瀏覽器進行下載。
@route('/download/:filename') def download(filename): return static_file(filename, root='/path/to/static/files', download=filename)
HTTP 錯誤與重定向
abort()
函數是建立 HTTP 錯誤頁面的快捷方式:
from bottle import route, abort @route('/restricted') def restricted(): abort(401, 'Sorry, access denied.')
要將瀏覽器請求的地址重定向其它的地址,你能夠向瀏覽器發送一個 303 see other
響應, redirect()
能夠實現這個功能:
from bottle import redirect @route('/wrong/url') def wrong(): redirect('/right/url')
除了 HTTPResponse 或者 HTTPError 異常外,還會有 500 Internal Server Error 響應。
Response
實例
響應的無數據如 HTTP 狀態碼,響應頭文件,或者 Cookies 都被保存在一個叫作 response
的對象中,並傳送給瀏覽器,你能夠直接操做這些無數據或者寫一些預約義的 helper 方法來處理它們。
狀態碼(Status Code)
HTTP 狀態碼 控制着瀏覽器處理方式,默認爲「200 OK」,絕大多數狀況下,你並不須要手工的去設置 Response.status
,可是使用abort()
函數或者返回一個 HTTPResponse
對象的時候,由於它們容許存在任何數值的狀態碼,爲了符合 HTTP 規範,咱們應該手動的爲其添加規範的 HTTP 狀態碼。
響應頭文件(Response Header)
響應的頭文件如 Cache-Control
或者 Location
等都是經過 @Response.set_header() 函數定義的,該函數接受兩個參數:一個頭文件名稱和一個值,名稱部分是區分大小寫的:
@route('/wiki/page') def wiki(page): response.set_header('Content-Language', 'en') ...
絕大多數頭文件都僅僅只能定義一次,可是有一些特別的頭文件卻能夠屢次定義,這個時候咱們在第一次定義時使用Response.set_header()
,可是第二次定義時,就須要使用 Response.add_header()
了:
response.set_header('Set-Cookie','name=value') response.add_header('Set-Cookie','name1=value1')
Cookies
你可使用 Request.get_cookie()
訪問已經設置了的 Cookie,可使用 Response.set_cookie()
設置 Cookie:
@route('/hello') def hello_again(self): if request.get_cookie('visited'): return 'Welcome back! Nice to see you again' else: response.set_cookie('visited','yes') return 'Hello there! Nico to meet you!'
Response.set_cookie()
方法接受一些特殊的參數,用來控制 Cookie 的生命週期或者行爲,最多見的一些參數以下:
- max_age : 該 Cookie 最大的生命期(按秒計算,默認爲 None)
- expires : 上個 datetime 對象或者一個 UNIX timestamp(默認爲 None)
- domain : 容許訪問該 Cookie 的域名(默認爲當前應用的域名)
- path : 按照路徑限制當前 Cookie(默認爲 「/「)
- secure : 限制當前Cookie僅僅容許經過 HTTPS 鏈接訪問(默認爲 off)
- httponly : 阻止瀏覽器端 Javascript 讀取當前 Cookie(默認爲 off,須要 Python 2.6 以上)
若是 expires 或者 max_age 都沒有設置的放在, Cookie 將在瀏覽器的會話結束後或者當瀏覽器關閉時失效,這裏還有一些問題是你在使用 Cookie 時須要考慮到的:
- 大多數瀏覽器都限制 Cookie 的大小不能超過 4Kb
- 有一些用戶設置了他們的瀏覽器不接受任何 Cookie,絕大多數搜索引擎也直接忽略 Cookie,你應該保證你的應用在沒有 Cookie 時也是可用的
- Cookie 保存在客戶端,而且沒有任何加密措施,你存放在 Cookie 中的任何內容,用戶都是可訪問的,若是有必要的話,攻擊者能經過 XSS 漏洞竊取用戶的 Cookie,因此,儘量在不要在 Cookie 中保存機密信息
- Cookie 是很容易被僞造的,因此,儘量不要想信 Cookie
就像上面看到的, Cookie 太容易被惡意軟件盜取,因此 Bottle 爲 Cookie 提供的加密方法,你所須要作的僅僅只是提供了一個密鑰,只要能確保該密鑰的安全便可,而其致使的結果是,對於未加密的 Cookie,@Request.get_cookie()@ 將返回 None。
@route('/login') def login(): username = request.forms.get('username') password = request.forms.get('password') if check_user_credentials(username, password): response.set_cookie('account', username, secret='some-sceret-key') return 'Welcome {}'.format(username) @route('/restricted') def restricted_area(self): username = request.get_cookie('account', secret='some-secret-key') if username: return 'Hello {}'.format(username) else: return 'You are not logged in.'
另外,Bottle 會自動 pickle 與 unpickle 你存儲到已簽名的 Cookie 上的數據,這表示你能夠向 Cookie 中存儲任何能夠 pickle 的數據對象,只要其大小不超過 4Kb便可。
訪問請求數據(Accessing Request Data)
Bottle 的全局對象 request
提供了對 HTTP相關的無數據如 Cookies, Headers, 或者 POST 表單數據的訪問,該對象在任什麼時候候都保存着當前請求的數據,只要其在一個路由的回調函數中訪問便可,它甚至還能夠在多線程環境中工做。
HTTP 頭文件
頭文件信息都保存在 Request.header 中,其成員是一個鍵區分大小寫的 HeaderDict
實例:
from bottle import route, request @route('js_ajax') def is_ajax(): if request.header.get('X-Requested-With') == 'XMLHttpRequest': return 'This is an AJAX request' else: return 'This is a normal request'
Cookies
Cookie 已一個普通的 dictionary 形式保存在 Request.COOKIES
對象中, Request.get_cookie()@ 方法能夠對簽名的 Cookie 進行訪問,下面示例展現了一個基於 Cookie 的訪問計數器:
from bottle import route, request, response @route('/counter') def counter(): count = int( request.COOKIES.get('counter', '0')) count += 1 response.set_cookie('counter',str(count)) return 'You visited this page {} times'.format(count)
查詢字符串(Query Strings)
查詢字符串經常被用來傳遞一些小數目的鍵值對參數到服務器,你可使用 Request.GET
字典對其進行訪問,使用 Request.query_string
來得到整個字符串:
from bottle import route, request, response @route('/forum') def display_forum(): forum_id = request.GET.get('id') page = request.GET.get('page','1') return 'Forum ID: {} ( Page: {} )'.format(forum_id, page)
POST 表單數據與文件上傳
POST 與 PUT 請求中, request 能夠包含各類編碼方式的數據,使用 Request.forms
對象能夠訪問普通的 POST 表單數據,文件上傳時提交的數據被單獨以 cgi.FieldStorage
實例的形式存儲在 Request.files
中,而 Request.body
按原始數據的方式保存有一個文件對象的數據。
下面是一個文件上傳的示例:
<form action"/upload" method="post" enctype="multipart/form-data"> <input type="text" name="name" /> <input type="file" name="data" /> <input type="submit" value="Upload" /> </form>
Bottle 代碼
from bottle import route, request @route('/upload', method = 'POST') def do_upload(): name = request.forms.get('name') data = request.files.get('data') if name and data.file: raw = data.file.read() #當文件很大時,這個操做將十分危險 filename = data.filename return "Hello {}! You uploaded {} ({} bytes).".format(name, filename, len(raw)) return "You missed a field"
WSGI 環境
Request
對象將 WSGI 環境數據都以 dictionary 等式保存在 Request.environ
中,容許你像訪問字典數據同樣訪問其值:
route('/my_ip') def show_ip(): ip = request.environ.get('REMOTE_ADDR') # 或者 ip = request.get('REMOTE_ADDR') # 或者 ip = request['REMOTE_ADDR'] return 'Your IP is : {}'.format(ip)
模板(Templates)
Bottle 內置了一個快速且強大的模板引擎,叫做:*SimpleTemplate Engine* ,你可使用 template()
函數 或者 view()
decorator 來編譯一個模板,你所要做的僅僅只是提供該模板,以及要傳送給模板的數據,下面是一個模板的簡單示例:
@route('/hello') @route('/hello/:name'): def hello(name = 'World') return template('hello', name = name)
上面的代碼將載入 hello.tpl ,而後將 name 傳送給該模板,並編譯它,再將結果返回給瀏覽器, Bottle 將在 ./views/ 或者bottle.TEMPLATE_PATH 設置的路徑中搜索模板文件。
view()
decorator 容許你返回一組須要傳送給模板的數據字典便可,而不須要再從新傳送模板名稱:
@route('/hello') @route('/hello/:name') @view('hello') def hello(name='World'): return dict(name=name)
模板語法
模板語法是很是精巧的,其工做原理基本能夠說成是:將模板文件中的代碼進行正確的縮進處理,以致你再也不須要擔憂塊縮進問題:
%if name == 'World': <h1> Hello {{name}} </h1> <p> This is a test.</p> %else: <h1>Hello {{name.title()}}</h1> <p>How are you?</p> %end
緩存
模板被編譯以後會緩存至內存中,你可使用 bottle.TEMPLATES.clear()
去手工清除它們。
插件(Plugins)
這是 Bottle 0.9 版本纔有的新功能,插件能夠提供 Bottle 核心同有提供的功能集,在「可用的 Bottle 插件列表」:http://bottlepy.org/docs/dev/plugins/index.html 中你能夠找到如今可用的插件,你還能夠開發本身的 Bottle 插件,好比 sqlite 插件,可讓你可使用 db
來訪問一個到SQLite 數據的連接:
from bottle import route, install, template from bottle_sqlite import SQLitePlugin install(SQLitePlugin(dbfile='/tmp/test.db'))
route('/show/:post_id') def show(db, post_id): c = db.execute('SELECT title, content FROM posts WHERE id = ?', (int(post_id),)) row = c.fetchone() return template('show_post', title=row['title'], text=row['content'])
route(’/contact’)
def contact_page(): ‘’‘該回調函數不須要任何數據庫鏈接,由於沒有 db 關鍵字, 因此 SQLite插件將徹底忽略該回調函數’‘’ return template(‘contact’)
在整個應用中安裝插件
插件能夠被安裝到整個應用中,或者僅僅只針對某幾個路由安裝,絕大多數插件都被安裝到整個應用中,覺得全部路由服務。要安裝一個插件,只須要將插件的名稱做爲第一個參數傳遞給 install()
函數便可:
from bottle_sqlite import SQLitePlugin install(SQLitePlugin(dbfile='/tmp/test.db'))
卸載已安裝的插件
你可使用名稱,類或者對象來卸載一個已經安裝的插件
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db') install(sqlite_plugin) uninstall(sqlite_plugin) #卸載特定的插件 uninstall(SQLitePlugin) #卸載該類的所的實例 uninstall('sqlite') # 卸載全部具備該名稱的插件 uninstall(True) # 一次性卸載全部已安裝的插件
插件能夠在任什麼時候間安裝與卸載,甚至是處理某個請求的回調函數中,每一次已經安裝的插件樹更新時, 路由緩存都會跟着更新。
與路由綁定的插件安裝
route()
的 apply
參數能夠指定某個回調函數要安裝的插件:
sqlite_plugin = SQLitePlugin(dbfile=’/tmp/test.db’)
@route(’/create’, apply=[sqlite_plugin])
def create(db): db.execute(‘INSERT INTO ….’)
插件黑名單
若是可使用 route()
方法中的 skip
參數指定插件黑名單,以下:
sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db') install sqlite_plugin) @route('/open/:db', skip=[sqlite_plugin]) def open_db(db): if db in ['test','test2']: sqlite_plugin.dbfile = '/tmp/{}.db'.format(db) return 'Database File Switched to : /tmp/{}.db'.format(db) abort(404, 'No such database')
插件與子應用
大多數插件都被安裝到須要它的具體的應用中,因此,它們不該該影響註冊給Bottle 應用的子應用:
root = Bottle() root.mount(apps.blog, '/blog') @route.route('/contact', template='contact') def contact(): return {'email':'contact@example.com') root.install(plugins.WTForms())
上面的示例代碼中,無論咱們何時 mount 一個子應用到主應用上,主應用都會爲子應用設定一個代理,因此上面的 WTForms 插件將只會影響到 ‘/contact’ 路徑,可是不會影響到 ‘/blog’ 子應用的全部URL。,可是這處理方式可使用下面的方法覆蓋:
route.mount(apps.blog, '/blog', skip=None)
開發(Development)
上面已經介紹了一些基本的關於 Bottle 的知識,若是你如今想使用 Bottle 開發本身的應用,那麼下面這些技巧對於你的項目來講可能頗有幫助:
默認應用
Bottle 維護着一份 Bottle
實例的棧,而 route()
實際上是對 Bottle.route()
的快捷訪問,以這種方法產生的路由都屬於默認應用:
@route('/') def hello(): return 'Hello World'
對於小應用來講,這已經足夠了,可是隨着應用的不斷增大,這種方法顯然不容易維護,因此咱們可使用子應用,將整個項目的功能細分:
blog = Bottle() @blog.route('/') def index(): return 'This is blog Index page'
將應用分離以後,程序的維護性提升了不少,並且可重用性也提升不少,其它的開發人員就能夠放心的從你的模塊中導入應用程序對象,並使用 Bottle.mount()
將你的應用與他們的應用整全到一塊兒。另一種替代方法,你可使用 應用棧
,這讓你能夠在全部子應用中都使用默認的 route 方法:
default_app.push() @route('/') def hello(): return 'Hello World' app = default_app.pop()
app()
與 default_app()
都是 AppStack
的實例,而且實現的類 Stack的API,你能夠 Push 或者 Pop應用到這個 stack 中。
Debug 模式
在開發的前期,Debug 模式將很是有助於你的開發:
bottle.debug(True)
在這種模式下,Bottle 能夠提供更多的 debugging 信息,即便程序出現一個錯誤,它同時還關閉了一些優化功能,添加了一些配置的檢測功能,下面是該模式不完整的功能列表:
- 默認錯誤頁面將返回一個對該錯誤的跟蹤
- 模板不會被緩存
- 插件將當即被安裝
自動重載
在開發的過程,你可能須要常常修改你的代碼,又常常須要重啓你的服務器以更新這些修改,Bottle 提供了一個自動重載的工具,這使得你對任何一個應用中的文件的修改都會被及時的更新到運行中的應用中:
from bottle import run run(reloader=True)
reloader
是這麼工做的: 主進程並不會啓動服務器,可是它會按照一樣的參數建立一個子進程,這使得全部模塊級的代碼都會被運行兩次。子進程的運行環境中會有一個叫做 os.environ['BOTTLE_CHILD'] = True
的參數,當任何一個已經加載的模塊有修改時,子進程會被中止,而後由主進程從新開啓新的子進程,對模板的修改將不會引起一次重載。
重載是基因而否能夠關閉子進程的,若是你運行在 Windows 或者任何其它不支持 signal.SIGINT
的操做系統上時,@signal.SIGTERM@ 被用來終止子進程。
部屬(Deployment)
Bottle 默認是運行在內置的 wsgiref WSGIServer上的,該無線程服務器對於開發來講再好不過了,可是對於日漸壯大的應用或者對於實際部屬來講,並非最好的選擇。
多線程服務器
提升效率的最快速的辦法,就是將應用部屬到一個多線程的服務器或者相似 Asynchronous WSGI 的服務器上,好比 paste 或者 cherrypy ,而且告訴 Bottle 以這些服務器啓動,而不是本身內置的服務器。
bottle.run(server='paste')
Bottle 支持不少服務器,下面列舉的並非全部的:
名稱 | 主頁 | 介紹 |
---|---|---|
cgi | 以CGI腳本運行 | |
flup | http://trac.saddi.com/flup | 以 FastCGI 進程運行 |
gae | http://code.google.com/appengine/docs/python/overview.html | Google App Engine 部屬 |
wsgiref | http://docs.python.org/library/wsgiref.html | 默認爲單線程的服務器 |
cherrypy | http://www.cherrypy.org/ | 多線程服務器 |
paste | http://pythonpaste.org/ | 多線程服務器 |
rocket | http://pypi.python.org/pypi/rocket | 多線程服務器 |
gunicorn | http://pypi.python.org/pypi/gunicorn | 部分用 C 編寫 |
fapws3 | http://www.fapws.org/ | Asynchronous,基於C 開發 |
tornado | http://www.tornadoweb.org/ | Asynchronous,服務了部分 FaceBook 的服務 |
twisted | http://twistedmatrix.com/ | Asynchronous |
diesel | http://dieselweb.org/ | Asynchronous,基於 Greenlet |
meinheld | http://pypi.python.org/pypi/meinheld | Asynchronous,部分基於 C 開發 |
bjoern | http://pypi.python.org/pypi/bjoern | Asynchronous,很是快,基於C開發 |
auto | 自動選擇一個可用的 服務器 |
完整的服務器名稱可以使用 server_names
變量得到,若是 Bottle 尚未提供你最喜歡的服務器,那你能夠手工的使用你的服務器啓動它:
from paste import httpserver httpserver.serve(bottle.default_app(), host='0.0.0.0', port = 80)
多服務器進程
一個 Python 進程只能使用到一個 CPU,即時服務器硬件有多個CPU,你能夠在不一樣的端口中啓動多個應用,每個應用使用一個 CPU,而後使用分流服務器對訪問進行分流,好比 Apache mod_wsgi 或者 Nginx 等均可以做爲前端分流服務器。