在Django裡面,使用Cookie和Session看起來好像是一樣的,使用的方式都是request.COOKIES[XXX]和request.session[XXX],其中XXX是您想要取得的東西的key,但是這兩者的原理和實現方式確是非常的不同。
首先介紹Cookie,只要是HTTP協議,就會有COOKIE這個東西; 只要您的浏覽器沒有禁用Cookie,就 可是使用它。而且是不分用什麼語言,用什麼框架,因為這是在HTTP協議的層面支持的,浏覽器會把您設置的XXX的這個Cookie在Response之後保存到您的本地機器,在每次您向服務器提交或者浏覽的時候會把上次保存下來的COOKIE帶上發送向服務器;說到這裡我們應該澄清一個概念,就是BS結構理論上是沒有同步的服務器-客戶端的狀態維持的,所以Cookie本質上就是一種異步的狀態維護,所有這一切浏覽器都幫我們搞定了,所以不用關心。 當然如果使用是Django的話,最終的HttpRequest也許是WSGIRequest(調試的時候使用WSGI方式),也許是ModPythonRequest(使用Apache+Mod_python的方式),甚至您自己寫自己的實現方式,但是最終我們都可以看到,他們都是一個HTTP協議要求的Requset的實現;
接下來介紹Session, 其他的框架當中是否也有Session這個東東我不知道,至少Django的這個東東是非常有用的。我們都知道Django可以同過meddleware來修改requset和response,如果想使用Django當中Session,首先必須要求您的Django工程的settiongs.py文件裡面的MIDDLEWARE_CLASSES設置裡面已經包含有django.contrib.sessions.middleware.SessionMiddleware(其實默認就是有的)。接下來我們看看/django/contrib/sessions/middleware.py這個文件,裡面定義了一個SessionMiddleware的class,其中的process_request函數之有一句話 request.session = SessionWrapper(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)),我們所使用的request.session當中的sesson這個變量就是在這一刻誕生的,其實這個session就是一個SessionWrapper類,這個類是根據得到COOKIES裡面的settings.SESSION_COOKIE_NAME來作為生成SessionWrapper的依據。
看到這裡大家留意沒有?session其實是依賴於Cookie的,如果浏覽器不支持Cookie的話,Django的Session也就無從用起了,因為Session的生成是根據Cookie裡面記錄的SESSION_COOKIE_NAME來生成的,那麼這個Cookie是什麼時候設置的呢? 接下來再看process_response過程,
def process_response(self, request, response):
# If request.session was modified, or if response.session was set, save
# those changes and set a session cookie.
patch_vary_headers(response, ('Cookie',))
try:
modified = request.session.modified
except AttributeError:
pass
else:
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
session_key = request.session.session_key or Session.objects.get_new_session_key()
if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
max_age = None
expires = None
else:
max_age = settings.SESSION_COOKIE_AGE
expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT")
new_session = Session.objects.save(session_key, request.session._session,
datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
response.set_cookie(settings.SESSION_COOKIE_NAME, session_key,
max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
return response
其他的不詳說了,我們注意其中的兩句
session_key = request.session.session_key or Session.objects.get_new_session_key() 如果存在session_key就用,如果不存在就創建一個新的Seesion,並返回它的key。
response.set_cookie(settings.SESSION_COOKIE_NAME, session_key,........) 把 response設置上這個settings.SESSION_COOKIE_NAME這個Cookie,改Cookie的值為session_key.
回過頭來我們再看看process_request,其實我們最初的第一次浏覽一個Django作的網站的時候,process_request函數是得不到叫做SESSION_COOKIE_NAME的這個Cookie的,但是在剩下的網站的第一次response之後,這個Cookie就出現了。process_request函數根據SESSION_COOKIE_NAME這個Cookie記錄的session_key來創建了一個SessionWrapper這個類的一個實例,以後我們使用的request.session就是這個SessionWrapper,其實SessionWrapper就是對Session這個Model的封裝(具體如何封裝我詳細講了,有興趣自己看一下),我們同過對request.session[XXX]=****這樣的操作最終到保存到了數據庫當中Session這個Model對應的表;
最後,既然有了Cookie,為什麼還需要Session呢?? 因為不能Cookie保存太多的東西,而且保存的類型僅限於字符串。比如我們使用request.COOKIES[XXX]=您自己創建的一個類的實例,這樣這個類的實例是絕對不能傳送到客戶端本地的,所以這時候session就派上用場了,這樣在客戶端的Cookie只是記錄了一個key,這個key用來說明服務器端的那個數據是這個客戶端的,至於服務器端的這個key的那條記錄,就可以保存N多東西了,因為這個數據是一個字典,同過pickle保存和還原。