文檔的這部分內(nèi)容將會向你展示如何使用 Werkzeug 最重要的部分。意在讓開發(fā)者對PEP 333 (WSGI) 和 RFC 2616 (HTTP) 有一個基本的了解。
警告
確保在文檔建議的地方導入所有對象。理論上從不同的地方導入對象是可行的,但是在這卻是不被支持的。
例如 MultiDict 是一個 werkzeug 模塊,但它在內(nèi)部卻不是 Werkzeug實現(xiàn)的。
WSGI 環(huán)境包含所有用戶向應用發(fā)送信息。你可以通過它向 WSGI 發(fā)送信息,但是你也可以使用 create_environ() 輔助函數(shù)創(chuàng)建一個 WSGI 環(huán)境字典:
>>> from werkzeug.test import create_environ
>>> environ = create_environ('/foo', 'http://localhost:8080/')
現(xiàn)在我們創(chuàng)造了一個環(huán)境:
>>> environ['PATH_INFO']
'/foo'
>>> environ['SCRIPT_NAME']
''
>>> environ['SERVER_NAME']
'localhost'
通常沒人愿意直接使用 environ 因為它對字節(jié)串是有限制的,而且不提供訪問表單數(shù)據(jù)的方法除非手動解析數(shù)據(jù)。
Request 對象訪問請求數(shù)據(jù)是很有趣的。它封裝 environ 并提供只讀的方法訪問數(shù)據(jù):
>>> from werkzeug.wrappers import Request
>>> request = Request(environ)
現(xiàn)在你可以訪問重要的變量,Werkzeug 將會幫你解析并解碼他們。默認的字符集是 utf-8但是你可以通過 Request 子類更改。
>>> request.path
u'/foo'
>>> request.script_root
u''
>>> request.host
'localhost:8080'
>>> request.url
'http://localhost:8080/foo'
我們也可以得到 HTTP 請求方法:
>>> request.method
'GET'
通過這個方法我們可以訪問 URL 參數(shù)(查詢的字符串) 和 POST/PUT 請求提交的數(shù)據(jù)。
為了測試,我們通過 from_values() 方法得到的數(shù)據(jù)創(chuàng)建一個請求對象:
>>> from cStringIO import StringIO
>>> data = "name=this+is+encoded+form+data&another_key=another+one"
>>> request = Request.from_values(query_string='foo=bar&blah=blafasel',
... content_length=len(data), input_stream=StringIO(data),
... content_type='application/x-www-form-urlencoded',
... method='POST')
...
>>> request.method
'POST'
我們可以很容易訪問 URL 參數(shù):
>>> request.args.keys()
['blah', 'foo']
>>> request.args['blah']
u'blafasel'
訪問提交的數(shù)據(jù)也是一樣的:
>>> request.form['name']
u'this is encoded form data'
處理上傳文件不再困難正如下例:
def store_file(request):
file = request.files.get('my_file')
if file:
file.save('/where/to/store/the/file.txt')
else:
handle_the_error()
files 代表一個 FileStorage 對象,提供一些常見的操作。
通過 headers 的屬性可以得到請求的 headers。
>>> request.headers['Content-Length']
'54'
>>> request.headers['Content-Type']
'application/x-www-form-urlencoded'
頭信息的鍵不區(qū)分大小寫。
這里還有更多 Werkzeug 提供的使用 HTTP headers 和其他請求數(shù)據(jù)的常用的方法。
讓我們用典型的 web 瀏覽器發(fā)送數(shù)據(jù)來創(chuàng)建一個請求對象。以便于更真實的測試:
>>> environ = create_environ()
>>> environ.update(
... HTTP_USER_AGENT='Mozilla/5.0 (Macintosh; U; Mac OS X 10.5; en-US; ) Firefox/3.1',
... HTTP_ACCEPT='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
... HTTP_ACCEPT_LANGUAGE='de-at,en-us;q=0.8,en;q=0.5',
... HTTP_ACCEPT_ENCODING='gzip,deflate',
... HTTP_ACCEPT_CHARSET='ISO-8859-1,utf-8;q=0.7,*;q=0.7',
... HTTP_IF_MODIFIED_SINCE='Fri, 20 Feb 2009 10:10:25 GMT',
... HTTP_IF_NONE_MATCH='"e51c9-1e5d-46356dc86c640"',
... HTTP_CACHE_CONTROL='max-age=0'
... )
...
>>> request = Request(environ)
讓我們從最沒有用(- -)的 headers 開始: the user agent:
>>> request.user_agent.browser
'firefox'
>>> request.user_agent.platform
'macos'
>>> request.user_agent.version
'3.1'
>>> request.user_agent.language
'en-US'
一個更有用的 headers 是 Accept header。這個 header 將會告訴 web 應用可以處理并怎么處理MIME類型,所有 accept header 被嚴格分類,最重要的是第一條:
>>> request.accept_mimetypes.best
'text/html'
>>> 'application/xhtml+xml' in request.accept_mimetypes
True
>>> print request.accept_mimetypes["application/json"]
0.8
可使用的語言也是一樣:
>>> request.accept_languages.best
'de-at'
>>> request.accept_languages.values()
['de-at', 'en-us', 'en']
當然還有編碼和字符集:
>>> 'gzip' in request.accept_encodings
True
>>> request.accept_charsets.best
'ISO-8859-1'
>>> 'utf-8' in request.accept_charsets
True
標準化是可行的,所以你可以安全的使用不同形式來執(zhí)行控制檢查:
>>> 'UTF8' in request.accept_charsets
True
>>> 'de_AT' in request.accept_languages
True
E-tags 和其他條件 header 也可以被解析:
>>> request.if_modified_since
datetime.datetime(2009, 2, 20, 10, 10, 25)
>>> request.if_none_match
<ETags '"e51c9-1e5d-46356dc86c640"'>
>>> request.cache_control
<RequestCacheControl 'max-age=0'>
>>> request.cache_control.max_age
0
>>> 'e51c9-1e5d-46356dc86c640' in request.if_none_match
True
Response 對象和請求對象相對。他常用于向客戶端發(fā)送響應數(shù)據(jù)。實際上,在 WSGI 應用中沒有什么比 Response 對象更重要了。
那么你要做的不是從一個 WSGI 應用中返回 returning 響應對象,而是在 WSGI 應用內(nèi)部調(diào)用一個 WSGI 應用并返回調(diào)用的值。
想象一個標準的 “Hello World” WSGI 應用:
def application(environ, start_res ponse):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello World!']
帶著一個響應對象的將會是這樣的:
from werkzeug.wrappers import Response
def application(environ, s tart_response):
response = Response('Hello World!')
return response(environ, start_response)
同時,不同與請求對象,響應對象被設計為可修改的。所以你還可以進行如下操作:
>>> from werkzeug.wrappers import Response
>>> response = Response("Hello World!")
>>> response.headers['content-type']
'text/plain; charset=utf-8'
>>> response.data
'Hello World!'
>>> response.headers['content-length'] = len(response.data)
你可以用同樣的方式修改響應狀態(tài),或者僅僅一個狀態(tài)嗎、一條信息:
>>> response.status
'200 OK'
>>> response.status = '404 Not Found'
>>> response.status_code
404
>>> response.status_code = 400
>>> response.status
'400 BAD REQUEST'
正如你看到的,狀態(tài)屬性是雙向的,你可以同時看到 status 和status_code ,他們相互對應的。
同時常見的 headers 是公開的,可以作為屬性訪問或者用方法設置/獲取他們:
>>> response.content_length
12
>>> from datetime import datetime
>>> response.date = datetime(2009, 2, 20, 17, 42, 51)
>>> response.headers['Date']
'Fri, 20 Feb 2009 17:42:51 GMT'
因為 etags 可以使 weak 或者 strong,所以這里有方法可以設置它:
>>> response.set_etag("12345-abcd")
>>> response.headers['etag']
'"12345-abcd"'
>>> response.get_etag()
('12345-abcd', False)
>>> response.set_etag("12345-abcd", weak=True)
>>> response.get_etag()
('12345-abcd', True)
一些有用的 headers 是可變的結(jié)構(gòu),比如 Content- header 是一個值的集合:
>>> response.content_language.add('en-us')
>>> response.content_language.add('en')
>>> response.headers['Content-Language']
'en-us, en'
下面的 header 值同樣不是單一的:
>>> response.headers['Content-Language'] = 'de-AT, de'
>>> response.content_language
HeaderSet(['de-AT', 'de'])
認證 header 也可以這樣設置:
>>> response.www_authenticate.set_basic("My protected resource")
>>> response.headers['www-authenticate']
'Basic realm="My protected resource"'
Cookie 同樣可以被設置:
>>> response.set_cookie('name', 'value')
>>> response.headers['Set-Cookie']
'name=value; Path=/'
>>> response.set_cookie('name2', 'value2')
如果頭出現(xiàn)多次,你可以使用 getlist() 方法來獲取一個 header 的所有值:
>>> response.headers.getlist('Set-Cookie')
['name=value; Path=/', 'name2=value2; Path=/']
最后如果你已經(jīng)設置了所有條件值,那么你可以根據(jù)一個請求作出響應。這意味著,如果一個請求可以確定已經(jīng)有了一個信息,只發(fā)送一個 header 是很節(jié)省流量的。盡管如此,你仍然應該至少設置一個 etag (用于比較) 和可以被請求對象的 make_conditional處理的 header 。
因此,響應是被改進的 (比如狀態(tài)碼改變,移除響應主題,刪除實體報頭等)。
更多建議: