關於Cookie和Session
簡單的說
- Cookie是儲存在瀏覽器的鍵值對
- Session是儲存在服務端的鍵值對
- Session是依賴於Cookie的
在Django框架中,我們可以直接操作cookie和session,但是tornado只支援cookie,那如果要使用session怎麼辦呢?自己定義session
基於記憶體實現SESSION
我們知道,在tornado
中,所有的請求都是由RequestHandler
物件來處理(以下簡稱handler
物件)。在RequestHandler
原始碼中,預留了一個鉤子方法initialize
,該方法會在例項化Handler
物件時執行。因此,如果我們繼承RequestHandler
類並重寫initialize
,就可以完成一些自定義操作。
import os import tornado.ioloop import tornado.web from tornado.web import RequestHandler import hashlib import time # 生成一個隨機的字串 def gen_random_str(): md5 = hashlib.md5() md5.update(str(time.time()).encode('utf-8')) return md5.hexdigest() class CacheSession(object): container = {} def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie("_session_id_") if client_random_str and client_random_str in self.container: self.random_str = client_random_str else: self.random_str = gen_random_str() self.container[self.random_str] = {} expires = time.time() + 300 self.handler.set_cookie("_session_id_", self.random_str, expires=expires) def __setitem__(self, key, value): self.container[self.random_str][key] = value def __getitem__(self, item): return self.container[self.random_str].get(item) class SessionHandler(RequestHandler): def initialize(self): # self指代的是當前的物件 self.session = CacheSession(self) class LoginHandler(SessionHandler,RequestHandler): def get(self, *args, **kwargs): self.render('login.html') def post(self, *args, **kwargs): username = self.get_argument('username') password = self.get_argument('password') if username == 'admin' and password == '123': self.session['username'] = username self.redirect('/main') else: self.redirect('/login') class MainHandler(SessionHandler,RequestHandler): def get(self, *args, **kwargs): username = self.session['username'] if username: self.write('this is main page') else: self.redirect('/login') def post(self, *args, **kwargs): pass settings = { "static_path" : os.path.join(os.path.dirname(__file__),'static'), "static_url_prefix":"static", "template_path":'views', "xsrf_cookies": True, } def make_app(): return tornado.web.Application([ (r"/login", LoginHandler), (r"/main", MainHandler), ], **settings) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
程式碼解釋
-
定義一個
Session
類,其例項化時接收handler
物件-
在
Session
類中定義一個靜態欄位(大字典),用來儲存session_id
和對應的使用者資訊;所有的session
物件都可以訪問這個大字典。 -
在
Session
的構造方法中,獲取和設定cookie
:-
呼叫handler物件
get_cookie()
方法獲取session_id
,如果沒有,則生成一段隨機字串random_str
作為session_id
-
將
session_id
寫入大字典 -
呼叫
handler
物件的set_cookie()
方法,通知瀏覽器設定cookie:set-cookie: {session_id: random_str}
-
呼叫handler物件
-
在
Session
類中,定義__getitem__
,__setitem__
,__delitem__
方法來實現通過字典的方式操作session
物件
-
在
-
在
initialize
方法中為handler
物件增加session
屬性,其值是Session
物件:self.session=Session(self)
;在每個路由對應的檢視中都重寫initialize
方法太麻煩了,利用面向物件的多繼承,將這一步單獨寫在一個類SessionHandler
,所以檢視類先繼承這個類即可 -
每次請求進來,都會執行
SessionHandler
中的initialize
方法,並例項化Session
物件,從而獲取session_id
-
操作
session
:-
通過
self.session[key] = value
, 即可呼叫session物件的__setitem__
方法來寫session
; -
通過
self.session[key]
即可呼叫session
物件的__getitem__
方法來獲取session
-
通過
del self.session[key]
即可呼叫session
物件的__delitem__
方法來刪除session
-
通過
基於Redis實現Session
import os import tornado.ioloop import tornado.web from tornado.web import RequestHandler import hashlib import time def gen_random_str(): md5 = hashlib.md5() md5.update(str(time.time()).encode('utf-8')) return md5.hexdigest() class RedisSession(object): @property def conn(self): import redis conn = redis.Redis(host='192.168.11.96', port=6379) return conn def __init__(self,handler): self.handler = handler client_random_str = self.handler.get_cookie("_session_id_") if client_random_str and self.conn.exists(client_random_str): self.random_str = client_random_str else: self.random_str = gen_random_str() expires = time.time() + 300 self.handler.set_cookie("_session_id_", self.random_str, expires=expires) self.conn.expire(self.random_str, 300) def __setitem__(self, key, value): self.conn.hset(self.random_str, key, value) def __getitem__(self, item): return self.conn.hget(self.random_str, item) class SessionHandler(RequestHandler): def initialize(self): self.session = RedisSession(self)
工廠模式實現Session
session_code.py
class SessionFactory(object): @staticmethod def get_session(): import settings import importlib engine = settings.SESSION_ENGINE module_path,cls_name = engine.rsplit('.',maxsplit=1) md = importlib.import_module(module_path) cls = getattr(md,cls_name) return cls
app.py
from session_code import SessionFactory cls = SessionFactory.get_session() print(cls)
setting.py
SESSION_ENGINE = "session_code.RedisSession" # SESSION_ENGINE = "session_code.CacheSession" SESSION_ID = "__session__id__" EXPIRERS = 300