flask-socketio与flask-session结合使用

我们在使用socketio时可以选择让socketio自己管理session或者是采用flask的session。在初始化Socketio时,可以加上manage_session这个关键字参数来决定session的管理方式,默认是True(即有socketio自己管理)
在默认情况下,flask-socketio可以修改在socket建立之前被push的上下文,但是这个修改仅限在socketio所定义的事件函数内有效,当在本次socket事件被触发时修改了session,那么修改语句以后的语句(包括下次同样的函数被触发)都可以得到被修改的数据,但是并不被保存到client,因为只有HTTP才可以设置cookie,下面是例子:

from flask import Flask, session, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
app.config['SECRET_KEY'] = 'DFSDFDSF'
socketio = SocketIO(app)


@app.route('/')
def index():
    session['a'] = 123
    return 'ok'


@app.route('/test/')
def chat():
    return render_template('a.html')  # a.html有定义的socket事件,可以触发下面的‘message'


@socketio.on('message')
def message(msg):
    print(session.get('a'))
    session['a'] = 555
    print(session.get('a'))


if __name__ == '__main__':
    socketio.run(app)

结果

123
555
123
555
555
555

首先访问首页,设置session[‘a’]=123,再次访问/test/,返回含有socket的html,然后触发’message’事件,首先打印session的值(也就是在首页设置的值),随后修改,再次打印可以看到,session已被修改,并且下次‘message’被触发,还是修改后的值。
当我们设置manage_session为False时,这个时候一般时在采用server-session的时候使用。通过flask-socket的源码可以看出,这么设置,可以在HTTP函数和SOCKET事件中共享session。即既可以在HTTP函数中修改session,也可以在socket事件中修改session,session的变化都能被这两个发现,下面是socket的事件触发时的部分源码

 def _handle_event(self, handler, message, namespace, sid, *args):
        if sid not in self.server.environ:
            # we don't have record of this client, ignore this event
            return '', 400
        app = self.server.environ[sid]['flask.app']
        with app.request_context(self.server.environ[sid]):
            if self.manage_session:
                # manage a separate session for this client's Socket.IO events
                # created as a copy of the regular user session
                if 'saved_session' not in self.server.environ[sid]:
                    self.server.environ[sid]['saved_session'] = \
                        _ManagedSession(flask.session)
                session_obj = self.server.environ[sid]['saved_session']
            else:
                # let Flask handle the user session
                # for cookie based sessions, this effectively freezes the
                # session to its state at connection time
                # for server-side sessions, this allows HTTP and Socket.IO to
                # share the session, with both having read/write access to it
                session_obj = flask.session._get_current_object()         **这里时为了后面判断,方便定义了这个变量**
            _request_ctx_stack.top.session = session_obj
            flask.request.sid = sid
            flask.request.namespace = namespace
            flask.request.event = {'message': message, 'args': args}
            try:
                if message == 'connect':
                    ret = handler()
                else:
                    ret = handler(*args)
            except:
                err_handler = self.exception_handlers.get(
                    namespace, self.default_exception_handler)
                if err_handler is None:
                    raise
                type, value, traceback = sys.exc_info()
                return err_handler(value)
            if not self.manage_session:
                # when Flask is managing the user session, it needs to save it
                if not hasattr(session_obj, 'modified') or session_obj.modified:
                    resp = app.response_class()
                    app.session_interface.save_session(app, session_obj, resp)
            return ret

当socket的事件被触发时,with app.request_context()自动把上下文push,由于整个app都被保存在self.server中,每次被push的session都是一样的(session里存的是memcached或者redis中的key,session中的值被存在value中),由于我们设置了manage_session为False 所以 session_obj = flask.session._get_current_object() 这是为了上端代码的后面的执行方便。可以看到上段代码的最后有保存session的动作,为了保存,首先需实例化一个response对象,代码到这里就需要下一个扩展了,因为server-session扩展都是在这里修改的接口,我们以flask-session为扩展并以memcached作为存储为例:

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        full_session_key = self.key_prefix + session.sid
        if PY2 and isinstance(full_session_key, unicode):
            full_session_key = full_session_key.encode('utf-8')
        if not session:
            if session.modified:
                self.client.delete(full_session_key)
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        expires = self.get_expiration_time(app, session)
        if not PY2:
            val = self.serializer.dumps(dict(session), 0)
        else:
            val = self.serializer.dumps(dict(session))


        self.client.set(full_session_key, val, self._get_memcache_timeout(
            total_seconds(app.permanent_session_lifetime)))
        if self.use_signer:
            session_id = self._get_signer(app).sign(want_bytes(session.sid))
        else:
            session_id = session.sid
        response.set_cookie(app.session_cookie_name, session_id,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

session.sid是一个uuid,可以看到在保存session的过程中,以session.sid+self.prefix 为key,将真正的session作为value存进去,在使用memcached时要注意,memcached的最长过期时间是30天,超过这个时间的存不进去,但是flask默认的permanent_session_lifetime是31天,虽然flask-session中有关于这个问题的做了兼容,但是似乎不起作用,毕竟两年前的扩展了,需要手动将permanent_session_lifetime设为小于31天。只要将session存到memcached中就可以做到共享session了。set_cookie在socket通信中是不会生效的。

你可能感兴趣的:(flask-socketio,flask-session)