Flask 学习-88. jsonify() 函数源码解读深入学习

Posted 上海-悠悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask 学习-88. jsonify() 函数源码解读深入学习相关的知识,希望对你有一定的参考价值。

前言

flask 有个jsonify() 函数,如果返回的是一个字典,那么调用 jsonify 创建一个响应对象。

关于响应

视图函数的返回值会自动转换为一个响应对象。
如果返回值是一个字符串,那么会被 转换为一个包含作为响应体的字符串、一个 200 OK 出错代码 和一个 text/html 类型的响应对象。
如果返回值是一个字典,那么会调用 jsonify() 来产生一个响应。以下是转换的规则:

  • 如果视图返回的是一个响应对象,那么就直接返回它。
  • 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
  • 如果返回的是一个字典,那么调用 jsonify 创建一个响应对象。
  • 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。
  • 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。

jsonify() 响应对象

如果返回的是一个字典,那么调用 jsonify 创建一个响应对象
先看一个示例

from flask import Flask, jsonify
app = Flask(__name__)


@app.route('/json', methods=['GET'])
def json_demo():
    return 
        "username": 'yoyo',
        "email": "111@qq.com"
    


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

接口返回

GET http://127.0.0.1:5000/json 

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 51
Server: Werkzeug/2.0.2 Python/3.8.5
Date: Thu, 13 Oct 2022 02:49:38 GMT


  "email": "111@qq.com", 
  "username": "yoyo"

接口返回的是一个dict, 那么这时候会自动调用 jsonify 创建一个响应对象转成json格式,并且在返回头部带上Content-Type: application/json

我们也可以返回jsonify()函数,里面传一个字典,或者键值对的参数

from flask import Flask, jsonify
app = Flask(__name__)


@app.route('/json1', methods=['GET'])
def json_demo1():
    return jsonify(
        "username": 'yoyo',
        "email": "111@qq.com"
    )


@app.route('/json2', methods=['GET'])
def json_demo2():
    return jsonify(
        username="yoyo",
        email="111@qq.com"
    )


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

以上两种方式返回的结果都是

Content-Type: application/json
Content-Length: 51
Server: Werkzeug/2.0.2 Python/3.8.5
Date: Thu, 13 Oct 2022 02:56:15 GMT


  "email": "111@qq.com", 
  "username": "yoyo"

返回json字符串

如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
我们试试自己返回一个json字符串,看看和jsonfy()处理过的有什么不一样

from flask import Flask, jsonify, json
app = Flask(__name__)


@app.route('/json3', methods=['GET'])
def json_demo3():
    return json.dumps(
        "username": 'yoyo',
        "email": "111@qq.com"
    )


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

接口返回内容

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 43
Server: Werkzeug/2.0.2 Python/3.8.5
Date: Thu, 13 Oct 2022 03:05:05 GMT

"email": "111@qq.com", "username": "yoyo"

此时返回的"Content-Type"类型是"text/html; charset=utf-8",它是以文本的方式返回的,并不是以json方式返回,所以需要我们自己手动再加一个返回头部类型

@app.route('/json3', methods=['GET'])
def json_demo3():
    return json.dumps(
        "username": 'yoyo',
        "email": "111@qq.com"
    ), "Content-Type": "application/json"

改成上面的代码,才会返回"Content-Type": "application/json"

也就是说jsonify() 函数帮我们在返回头部自动带上"Content-Type": "application/json", 简化了操作。

jsonify() 源码解读

先看源码内容

def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response":
    """Serialize data to JSON and wrap it in a :class:`~flask.Response`
    with the :mimetype:`application/json` mimetype.

    Uses :func:`dumps` to serialize the data, but ``args`` and
    ``kwargs`` are treated as data rather than arguments to
    :func:`json.dumps`.

    1.  Single argument: Treated as a single value.
    2.  Multiple arguments: Treated as a list of values.
        ``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``.
    3.  Keyword arguments: Treated as a dict of values.
        ``jsonify(data=data, errors=errors)`` is the same as
        ``jsonify("data": data, "errors": errors)``.
    4.  Passing both arguments and keyword arguments is not allowed as
        it's not clear what should happen.

    .. code-block:: python

        from flask import jsonify

        @app.route("/users/me")
        def get_current_user():
            return jsonify(
                username=g.user.username,
                email=g.user.email,
                id=g.user.id,
            )

    Will return a JSON response like this:

    .. code-block:: javascript

        
          "username": "admin",
          "email": "admin@localhost",
          "id": 42
        

    The default output omits indents and spaces after separators. In
    debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``,
    the output will be formatted to be easier to read.

    .. versionchanged:: 2.0.2
        :class:`decimal.Decimal` is supported by converting to a string.

    .. versionchanged:: 0.11
        Added support for serializing top-level arrays. This introduces
        a security risk in ancient browsers. See :ref:`security-json`.

    .. versionadded:: 0.2
    """
    indent = None
    separators = (",", ":")

    if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
        indent = 2
        separators = (", ", ": ")

    if args and kwargs:
        raise TypeError("jsonify() behavior undefined when passed both args and kwargs")
    elif len(args) == 1:  # single args are passed directly to dumps()
        data = args[0]
    else:
        data = args or kwargs

    return current_app.response_class(
        f"dumps(data, indent=indent, separators=separators)\\n",
        mimetype=current_app.config["JSONIFY_MIMETYPE"],
    )

大概翻译下,jsonify 的作用是把数据序列化成JSON,并且在声明返回头部application/json,它返回一个Response 对象
它使用json.dumps 序列化数据, 但是 argskwargs 会被作为数据,而不是参数
1.如果是单个参数

jsonify('xx')

那么返回

Content-Type: application/json


"xx"

2.如果是多个参数

jsonify(1, 2, 3)

那么等价于

jsonify([1, 2, 3])

都会返回

Content-Type: application/json

[
  1, 
  2, 
  3
]

3.关键字参数

jsonify(data=data, errors=errors)

等价于

jsonify("data": data, "errors": errors)

4.不允许同时传递args`参数和 kwargs``关键字参数,因为不清楚会发生什么.

默认情况下JSON输出会省略在分隔符后缩进和空格,起到了压缩数据的作用。(实际上就是用了 json.dumps(data, separators=(‘,’, ‘:’)) 处理。)
在debug模式或者我们设置了全局配置:JSONIFY_PRETTYPRINT_REGULARTrue,输出将被格式化以便于阅读。
示例,debug为False

@app.route('/json1', methods=['GET'])
def json_demo1():
    return jsonify(username='yoyo',email="111@qq.com")


if __name__ == '__main__':
    app.run(debug=False)

启动日志看到debug模式关闭了

 * Serving Flask app 'app' (lazy loading)
 * Environment: development
 * Debug mode: off

次数输出的json格式会被去掉空格和缩进,可以起到压缩数据作用

Content-Type: application/json


"email":"111@qq.com","username":"yoyo"

为了方便阅读,我们可以开启debug模式,或者添加全局配置JSONIFY_PRETTYPRINT_REGULARTrue

if __name__ == '__main__':
    app.run(debug=True)

启动日志

 * Serving Flask app 'app' (lazy loading)
 * Environment: development
 * Debug mode: on

此时返回的json格式就是被美化过

"email":"111@qq.com","username":"yoyo"

其原理实际上就是用了 json.dumps()的 indent和 separators=(‘,’, ‘:’)处理

使用总结

总的来说有以下几点功能
1.如果返回值是一个字典,那么会调用 jsonify() 来产生一个响应
2.jsonify 返回的Response的headers属性为:Content-Type: application/json ,是标准的json格式。
3.jsonify自动去除了JSON格式中逗号冒号间的空格,起到了压缩数据的作用。

以上是关于Flask 学习-88. jsonify() 函数源码解读深入学习的主要内容,如果未能解决你的问题,请参考以下文章

Flask 学习-6. jsonify()返回JSON格式数据

Python json函数与Flask jsonify函数

为啥flask的jsonify方法很慢?

将用户构建的 json 编码器传递到 Flask 的 jsonify

Flask 学习-7. make_response() 自定义响应内容

使用 Flask 的 jsonify 时,将 datetime.date 保持为 'yyyy-mm-dd' 格式