简单的说
- 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
代码语言:javascript复制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
代码语言:javascript复制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 clsapp.py
代码语言:javascript复制from session_code import SessionFactory
cls = SessionFactory.get_session()
print(cls)setting.py
代码语言:javascript复制SESSION_ENGINE = "session_code.RedisSession"
# SESSION_ENGINE = "session_code.CacheSession"
SESSION_ID = "__session__id__"
EXPIRERS = 300


