Tornado 是我非常喜欢的一个框架,但是它缺失了很多功能模块,比如说 Session,正因为它啥都没有,所以我就爱上它了,这样可以方便自己撸轮子。:D
# Session 原理Session 是由于 HTTP 协议是无状态的,所以需要一种机制来保持客户端和服务器之间的会话。
HTTP 协议的流程是
Client -> Send Request -> Server -> Return Response
上述过程,是一次性的,也就是说当客户端发送请求直到服务器返回响应之后,客户端和服务器之间就再也没有任何联系了。于是一般人都会想到,在每次请求时带上一个特定的标识符,然后服务器端记录下来,每次根据特定的标志符就可以知道客户端的身份了。那么,这个特定的标志符就是 session id。这个就是大致上 session 的原理了。
一般情况下,session id 是储存在 cookie 里,并且通过 HTTP Header 传递给服务器,当然,也可以作为 HTTP Query parameters,只是这样个人觉得不太优雅,当然也有一些不安全的因素存在。
# 为 Tornado 添加 Session 知道了 Session 的原理后,就可以很方便的为 Tornado 添加 Session 机制了。 ## SessionMixin 下面是一个 SessionMixin 类,作用是开启 Session 和保存 Session,和 PHP 的 `session_start` 简直是一样一样的作用,其中 `self.session_storage` 是服务器的 Session 储存容器。 ``` python class SessionMixin(object):def open_session(self):
self._session_name = self.get_secure_cookie('tornado-session')
self.session = self.session_storage.get(self._session_name) or {}
self._session_age = 3600 * 24 * 31
def set_session_age(self, expires):
self._session_age = expires
def save_session(self):
if not getattr(self, '_session_name'):
self._session_name = str(uuid.uuid4())
self.set_secure_cookie('wing2-session', self._session_name)
self.session_storage.set(self._session_name, self.session, self._session_age)
<div id="with-tornado"></div>
## 结合 Tornado
有了 `SessionMixin` 之后,就是结合 Tornado 的使用了。
``` python
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
session_storage = dict()
def prepare(self):
self.open_session()
if self.session.get('user_id'):
user_id = self.session['user_id']
self.current_user = UserModel.get_by_user_id(user_id)
def finish(self, chunk=None):
self.save_session()
super(BaseHandler, self).finish(chunk)
上面是初始化 Session,然后继承 BaseHandler
,并且在 RequestHandler 里使用 self.session
就可以操作 Session 了。
首先是一个抽象的 Session Storage 的类
class SessionStorage(object):
def get(self, key, default=None):
raise NotImplemented
def set(self, key, value, expires=None):
raise NotImplemented
def delete(self, key):
raise NotImplemented
基本实现上述 3 个方法,就可以成为一个 Session 的容器了。
以下是 Redis 作为容器的示例
class RedisStorage(SessionStorage):
def __init__(self, redis, prefix="wing2"):
self._redis = redis
self._prefix = prefix
def _wrapper(self, key):
return "session:{prefix}:{key}".format(
prefix=self._prefix,
key=key
)
def get(self, key, default=None):
key = self._wrapper(key)
value = self._redis.get(key)
if not value:
return default
value = pickle.loads(value)
return value
def set(self, key, value, expires=None):
key = self._wrapper(key)
value = pickle.dumps(value)
return self._redis.setex(key, value, expires or 86400) # default one day
def delete(self, key):
key = self._wrapper(key)
return self._redis.delete(key)
以上就是为 Tornado 添加 Session 的示例。
# 感谢 * Flask Session (https://github.com/pallets/flask/blob/master/flask/sessions.py)EOF