为什么Flask会话的值必须是JSON可序列化的?

为什么Flask会话的值必须是JSON可序列化的?

问题描述:

我正在尝试为Flask应用程序中的用户会话实例化一个基本的Model实例.我对类必须是JSON可序列化的要求感到措手不及.我以为会话字典只是用于存储会话信息的任意构造,但是听起来它的使用受到更多限制,其中之一显然是JSON可序列化的值.还有哪些其他约束,此JSON约束的目的到底是什么?Web应用程序是否很难通过JSON保留用户会话?

I'm trying to instantiate a basic Model instance for the user session in my Flask application. I'm caught off guard by the requirement that my class be JSON serializable. I thought the session dictionary was just an arbitrary construct to store session information but it sounds like there's more constraints surrounding its usage, one of which is apparently the values be JSON serializable. What other constraints are there and what is the purpose of this JSON constraint exactly? Is it a hard expectation for web apps to persist their user sessions via JSON? Where does this requirement come from/is inspired by?

@app.route( '/' , methods=['GET', 'POST'] )
def index():
    """index  takes user to the home page of the application.
    """

    # Create model instance for this user session
    if 'model' not in session :
        session['model'] = Model( )


File "C:\Anaconda3\lib\site-packages\flask\json.py", line 83, in default
return _json.JSONEncoder.default(self, o)
File "C:\Anaconda3\lib\json\encoder.py", line 173, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <Model.Model object at 0x000000000770B6D8> is not JSON serializable

就其他背景而言,会话对象似乎是一个LocalProxy实例,可能会暗示事物的设计.

Just for additional background it looks like the session object is a LocalProxy instance which may clue into the design of things.

>>> type( session )
<class 'werkzeug.local.LocalProxy'>

好,因此在Miguel Grinberg的 Flask Workshop .对JSON支持的需求与持久保存此会话数据有关,后者作为客户端上的cookie.因此,数据必须进行序列化以弥合服务器和客户端之间的差距.据他说...

Ok, so there's some good commentary on this in Miguel Grinberg's Flask Workshop at Pycon 2015. The need for JSON support has to do with where this session data is persisted, which is as a cookie on the client. Therefore the data has to serialize to bridge the gap between server and client. According to him...

因此,flask支持此会话对象,...在任何路由功能内在处理请求时,您将会话称为字典.您写的任何内容都会被记住,下次您可以寻找它它将在那里,并且将针对特定用户.

So flask supports this session object,... inside any route function while your handling a request you refer to session as a dictionary. And anything you write is remembered, next time you can look for it and it will be there and it will be user specific.

如果您想知道它是如何工作的,这是Flask的内部原理,您在这本词典中写的任何内容,Flask都会放入Cookie中,它将发送给客户.因此,这将存储在客户端的网页浏览器.这将是经过冰冻签名的cookie,以确保用户不篡改它,没有可能发生攻击.您可以查看Cookie中的内容,但看不到修改它.默认情况下,Flask将数据本身写入Cookie中,然后该Cookie会发送给客户端.其他许多框架的作用默认是不同的.基本上,他们要做的就是将数据写入服务器中的文件或数据库,然后他们用标识该数据的ID,然后他们将ID发送到曲奇饼.因此,Flask默认将所有数据发送到Cookie中,因此无需在服务器中存储任何内容.

If you're interested to know how that works, this is Flask internals, anything you write to this dictionary, Flask will put in a cookie and it will send it to the Client. So this will be stored in the client's web browser. It will be a cookie that is crytographically signed, to make sure that the user does not tamper with it, that there are no attacks possible. You can see what's in the cookie but you cannot modify it. By default Flask writes the data itself in the cookie and that cookie goes to the client. What many other frameworks do by default is different. Basically what they do is they write the data in a file or database in the server, and then they write a cookie with an id that identifies that data, and then they send the id in the cookie. So Flask by default sends the whole data in the cookie so there's no need to store anything in the server.

因此,在我的特殊情况下,为了解决这个问题,我必须实现一个JSONEncoder并先对其进行编码,然后再将其传输到我的会话中

So in my particular case to get around this I had to implement a JSONEncoder and first encode it before transferring it to my session

<<Model.py>>
class ModelEncoder( JSONEncoder ) :
    def default( self , obj ) :
        if isinstance( obj , Model ):
            return obj.to_json()
        # Let the base class default method raise the TypeError
        return json.JSONEncoder.default( self , obj )

class Model( JSONEncoder ) :
    ....
    def to_json( self ) :
        """
        to_json transforms the Model instance into a JSON string
        """
        return jsonpickle.encode( self )


<<FlaskApp.py>>
    ...
    # Create model instance for this user session
    if 'model' not in session :
        session['model'] = ModelEncoder().encode( Model( ) )