回顧 HTTP 協議的通信核心,無非就是請求報文和響應報文之間的交互。而請求報文由客戶端生成,也就是用戶的瀏覽器;響應報文則由服務器生成,作為web應用的開發者,大多數工作就是構造一個合適的響應報文。在 django 中,請求報文已經被封裝成了 HttpRequest 對象,該對象的創建是自動的,且會傳遞給視圖函數作為第一個參數。而 HttpResponse 對象則需要 web 開發者自己創建,一般在視圖函數中 return 回去。下面我們就來看看 HttpResponse 對象的各種細節。
首先,這個對象由 HttpResponse 類創建,這個類位於 django.http 模塊中,所以在使用的時候還先從模塊中導入這個類。
例如:
from django.http import HttpResponse
然后,我們需要知道傳遞什么參數,這個時候先看看其構造函數是怎么樣的。
HttpResponse.__init__(content='', content_type=None, status=200, reason=None, charset=None)
content:可以是一個迭代器或字符串。如果是一個迭代器,HttpResponse 將立即處理這個迭代器, 把它的內容轉成字符串,並丟棄這個迭代器。如果你需要從迭代器到客戶端的數據響應以數據流的形式, 你必須用 StreamingHttpResponse 類代替;如果是一個字符串(迭代器處理后的或手動傳入的),那么這個字符串將作為相應報文的主體內容,也就是說如果是一個 http 文檔,那么這個文檔將會放入響應報文的主體中,最后在瀏覽器中顯示,這也是最為常用的方式之一。
content_type:用於指定 MIME 類型和編碼,例如:“text/html; charset=utf-8”。客戶端需要知道主體是什么類型的資源,才能調用相應的插件或內置的程序去處理。如果不傳入,也就是為 None 時,將使用 DEFAULT_CONTENT_TYPE 的值來指定 MIME 類型,這個值默認為:'text/html';使用 DEFAULT_CHARSET 的值來指定文件編碼,默認為:'utf-8'。
status:響應狀態碼,200代表成功,一般不需要改變,除非有特殊要求。
reason:原因短語,也就是 200 ok 中的 ‘ok’,因為客戶端是根據狀態碼來判斷響應是否成功的,所以 reason 的影響幾乎為 0 ,只是對人的提醒而已。如果沒有指定, 則使用默認響應短語。也就是 200 就對應於 ok,404 就對應於 not found。
charset:在response中被編碼的字符集。如果沒有給定(也就是為None),將會從 content_type 中提取,如果提取不成功, 那么 DEFAULT_CHARSET 的設定將被使用。
同樣的,我們可以使用相關的屬性去查看這些值:
HttpResponse.content :表示內容的字符串,對應於我們傳入的 content 參數的值。
HttpResponse.charset :一個表示編碼的字符串,對應於 charset 參數,如果實例化的時候沒有給定,將從 content_type 中解析出來,如果解析失敗,將使用 DEFAULT_CHARSET 的值。
HttpResponse.status_code :表示響應的狀態碼。在 1.9 中除非 reason_phrase 屬性被顯式的設置,否則在構造函數外修改狀態碼時,也會修改 reason_phrase 屬性。也就是說,當我們在創建實例的時候,並沒有設置 reason ,原來的狀態碼是 200 ,當我們在構造器外修改這個屬性的時候,如修改成 404,那么 reason_phrase 屬性就變成對應的 not found。
HttpResponse.reason_phrase :表示響應的原因短語,對應於 reason 參數。在1.9中 reason_phrase 不再默認為全部大寫字母。現在使用 HTTP 標准的默認原因短語。除非顯式設置,reason_phrase 由status_code 的值的確定。
HttpResponse.streaming :這個選項總是 False。由於這個屬性的存在,使得中間件(middleware)能夠區別對待流式 response 和常規 response 。
HttpResponse.closed :如果響應已關閉,則是 True 的。
HttpResponse.__setitem__(header, value)
由給定的首部名稱和值設定相應的報文首部。 header 和 value 都應該是字符串類型。
HttpResponse.__delitem__(header)
根據給定的首部名稱來刪除報文中的首部。如果對應的首部不存在將沉默地(不引發異常)失敗。不區分大小寫。
HttpResponse.__getitem__(header)
根據首部名稱返回其值。不區分大小寫。
HttpResponse.has_header(header)
通過檢查首部中是否有給定的首部名稱(不區分大小寫),來返回 True 或 False 。
HttpResponse.setdefault(header, value)
設置一個首部,除非該首部 header 已經存在了。
另外,我們還可以使用類字典的方法來設置首部,例如:
>>> response = HttpResponse() >>> response['Age'] = 120 >>> del response['Age']
注意:當調用 del 去刪除的首部不存在時,會引發 KeyError
異常。
但是,當我們設定 Cache-Control 首部和 Vary 首部時,推薦使用 patch_cache_control() 和 patch_vary_headers()方法,可以從 django.utils.cache 中導入它們。因為這兩個首部通常有多個值,這些值都要逗號隔開。"patch" 方法可以確保這些值,例如由中間件添加的值不會被改變。而字典形式添加的,同名鍵的不同值會發生沖突,只有最后一個值有效。
另外,雖然標准的 HTTP 報文中要求首部的每一行都要使用換行符隔開,但是 django 已經幫我們做了這些,所以我們不用重復的添加換行符了,否則會觸發 BadHeaderError 異常。
HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)
設置一個Cookie。參數與Python 標准庫中的 Morsel Cookie 對象相同。
key:鍵名,字符串形式。
value:對應的值,字符串形式。
max_age:cookie 過期的相對時間,單位是秒。如果為None,則當瀏覽器關閉的時候過期。如果設置了 max_age 而沒有設置 expires,則 expires 將根據 max_age 的值計算出來。
expires:設置 cookie 過期的絕對時間。應該是一個 UTC "Wdy, DD-Mon-YY HH:MM:SS GMT" 格式的字符串,或者一個 datetime.datetime 對象。如果 expires 是一個datetime 對象,則 max_age 會通過計算得到。
path:一個字符串,表示客戶端回送 cookie 的路徑,如果為‘/’,則表示該域名下的所以路徑都將回送 cookie,如果是‘/blog/’;則在訪問‘/blog/abc’或者‘/blog/def’等,所有包含該前綴的路徑時,客戶端都會回送 cookie。
domain:cookie有效的域。例如,其值為‘.scolia.com’時,那么在訪問 www.scolia.com 或者 test.scolia.com 之類的時,都會回送 cookie ,當然通常會和 path 配合在一起使用。根據 HTTP 協議的要求,這個值必要要兩到三個句點,從而防止出現 ‘.com’、‘.edu’、‘va.us’等形式的域名。當域為高層域時,只要兩個句點就可以了,而高層域包括:.com、.edu、.net、.org、.gov、.mil、.int、.biz、.info、.name、 .museum、.coop、.aero、和.pro。其他的域則需要至少三個。
secure:當其為 True 時,表示只要在 https 連接的情況下才會回送cookie
httponly:當其為 True 時,JavaScript 等就不能訪問對應的cookie了。當然這個標記並不是cookie標准中的,但目前市面上常用的瀏覽器都支持。靈活使用可以提供數據的安全性。
注意:
RFC 2109 和RFC 6265 都聲明客戶端至少應該支持 4096 個字節的Cookie。對於許多瀏覽器,這也是最大的大小。如果視圖存儲大於 4096 個字節的 Cookie,Django 不會引發異常,但是瀏覽器將不能正確設置 Cookie。
HttpResponse.set_signed_cookie(key, value, salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
與 set_cookie() 類似,但是在設置之前將用密鑰簽名,也就是常說的加鹽處理。通常與 HttpRequest.get_signed_cookie() 一起使用。你可以使用可選的 salt 參數來增加密鑰強度,但需要記住在調用 HttpRequest.get_signed_cookie() 時,也要把使用的 salt 參數傳入,用於解密。
HttpResponse.delete_cookie(key, path='/', domain=None)
在cookie中刪除指定的 key 及其對應的 value。如果 key 不存在則什么也不發生,也就是不會引發異常。
由於 Cookie 的工作方式,path 和 domain 應該與 set_cookie() 中使用的值相同 —— 否則 Cookie 不會刪掉。
HttpResponse.write(content)
將 content 寫到報文的主體中,這使得 HttpResponse
的實例類似於文件對象。
類似的還有:
HttpResponse.flush() :將緩存區的內容寫入到報文中
HttpResponse.tell() :移動文件中的操作指針
HttpResponse.getvalue()
從 HttpResponse.
content 中返回其值。這使 HttpResponse 的實例類似於數據流對象。
HttpResponse.writable()
總是 True,表示其是可寫的。這使 HttpResponse 的實例類似於數據流對象。
HttpResponse.writelines(lines)
在內容中寫入一行。不添加行分隔符。這使 HttpResponse 的實例類似於數據流對象。
HttpResponse的子類
Django包含了一系列的HttpResponse衍生類(子類),用來處理不同類型的HTTP 響應(response)。與 HttpResponse 相同, 這些衍生類(子類)存在於 django.http 之中。
class HttpResponseRedirect
構造函數的第一個參數是必要的 — 用來重定向的地址。這些能夠是完全特定的URL地址(比如,'http://www.yahoo.com/search/'),或者是一個不包含域名的絕對路徑地址(例如, '/search/')。關於構造函數的其他參數,可以參見 HttpResponse。注意!這個響應會返回一個302的HTTP狀態碼。
url:一個只讀屬性,表示代表響應將會重定向的URL地址(相當於Location 首部信息)。
class HttpResponsePermanentRedirect
與 HttpResponseRedirect 一樣,但是它會返回一個永久的重定向(HTTP狀態碼301)而不是一個“found”重定向(狀態碼302)。
class HttpResponseNotModified
構造函數不會有任何的參數,並且不應該向這個響應(response)中加入內容(content)。使用這個意味着資源在用戶最后一次請求之后,沒有修改過(狀態碼304)。
class HttpResponseBadRequest
與HttpResponse的行為類似,但是使用了一個400的狀態碼。表示一個錯誤的請求。
class HttpResponseNotFound
與HttpResponse的行為類似,但是使用了一個404的狀態碼。表示資源沒有找到。
class HttpResponseForbidden
與HttpResponse的行為類似,但是使用了一個403的狀態碼。表示用戶無權訪問。
class HttpResponseNotAllowed
與HttpResponse的行為類似,但是使用了一個405的狀態碼。表示請求的方法不被允許。 構造函數的第一個參數是必須的:一個允許使用的方法構成的列表(例如,['GET', 'POST'])。也就是說不在列表中的方法就是不被運行的方法。
class HttpResponseGone
與HttpResponse的行為類似,但是使用了一個410的狀態碼。表示服務器曾經擁有過此資源。主要是在 web 站點進行維護的時候,通知客戶端。
class HttpResponseServerError
與HttpResponse的行為類似,但是使用了一個500的狀態碼。表示服務器內部錯誤。
注意:如果一個自定義的 HttpResponse 的子類實現了 render 方法,那么 django 會將其當作一個 SimpleTemplateResponse。 render 方法必須返回一個有效的響應對象。
JsonResponse
json是目前常用的一種數據格式,有時候我們需要返回一個json格式的數據,而 JsonResponse 提供了一個快捷的方法。
它是 HttpResponse 的一個子類,用來幫助用戶創建JSON 編碼的響應。它從父類繼承大部分行為,下面看起構造函數:
class JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs)
data:應該傳遞一個標准的 python 字典給它,它將其轉換成 json 格式的數據。
encoder:默認為 django.core.serializers.json.DjangoJSONEncoder,用於序列化data。關於這個序列化的更多信息參見JSON 序列化。
safe : 默認為True。如果設置為False,可以傳遞任何對象進行序列化(否則,只允許dict 實例)。如果safe 為True,而第一個參數傳遞的不是dict 對象,將拋出一個TypeError。
另外:它的默認 Content-Type 頭部設置為application/json。
json_dumps_params:在1.9版本中新增,可以傳遞一個python標准的 json 庫中,json.dump() 方法處理后的對象給它,用於生成一個響應。
用法:
典型的用法如下:
>>> from django.http import JsonResponse >>> response = JsonResponse({'foo': 'bar'}) >>> response.content '{"foo": "bar"}'
序列化非字典對象:
若要序列化非dict 對象,你必須設置safe 參數為False:
>>> response = JsonResponse([1, 2, 3], safe=False)
如果不傳遞safe=False,將拋出一個TypeError。
注意:
在EcmaScript 第5版之前,這可能會使JavaScript Array 構造函數崩潰。出於這個原因,Django 默認不允許傳遞非字典對象給JsonResponse 構造函數。然而,現代的大部分瀏覽器都已經實現EcmaScript 5,它刪除了這種攻擊性的數組。所以可以不用關注這個安全預防措施。
修改默認的JSON 編碼器:
如果你需要使用不同的JSON 編碼器類,你可以傳遞encoder 參數給構造函數:
>>> response = JsonResponse(data, encoder=MyJSONEncoder)
StreamingHttpResponse
class StreamingHttpResponse
StreamingHttpResponse類被用來從Django流式化一個響應(response)到瀏覽器。當生產的響應太長或者占用太多的內存的時候,你可能會使用到它。例如,它對於生成大型CSV文件非常有用。
性能方面的考慮:
django是為了短鏈接而設計的,也就是說每次響應完畢之后都會斷開連接。流式響應將會為整個響應期協同工作進程。這可能導致性能變差。
總的來說,你需要將代價高的任務移除 請求—響應 的循環,而不是求助於流式響應。
StreamingHttpResponse 不是 HttpResponse 的衍生類(子類),因為它實現了完全不同的應用程序接口(API)。盡管如此,除了以下的幾個明顯不同的地方,其他幾乎完全相同:
- 應該提供一個迭代器給它,這個迭代器生成出字符串將用來構成內容(content)
- 你不能直接訪問它的內容(content),除非迭代響應對象本身。這只在響應被返回到客戶端的時候發生。
- 它沒有 content 屬性。取而代之的是,它有一個 streaming_content 屬性。
- 你不能使用類似文件對象的tell()或者 write() 方法。那么做會拋出一個異常
StreamingHttpResponse
應該只在下面的情況下使用:請求是獨立的,並且整個內容是不能重復的,在發生給客戶端之前。因為其內容(content)是不能訪問的,所以很多中間件是無法正常工作的。例如:ETag 和 Content- Length 首部就不能在流式相應中生成。
屬性:
StreamingHttpResponse.streaming_content
一個迭代器,包含內容字符串。
StreamingHttpResponse.status_code
響應的狀態碼
StreamingHttpResponse.reason_phrase
響應的原因短語
StreamingHttpResponse.streaming
總是True,表示其是一個流式響應。
FileResponse
class FileResponse
FileResponse是StreamingHttpResponse的衍生類(子類),為二進制文件做了優化。如果 wsgi server 來提供,則使用了wsgi.file_wrapper ,否則將會流式化一個文件為一些小塊。
FileResponse 需要通過二進制模式打開文件,如下:
>>> from django.http import FileResponse >>> response = FileResponse(open('myfile.png', 'rb'))