• 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格式中逗号冒号间的空格,起到了压缩数据的作用。

  • 相关阅读:
    线程池参数详解
    线程池各个参数详解以及如何自定义线程池
    fastdfs 安装
    SQL 执行顺序
    《SQL 进阶教程》 查找局部不一致的数据
    redis 高性能的原因
    一致性hash
    环境部署数据库报错
    redis 常用命令
    redis 高级学习和应用场景
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16787733.html
Copyright © 2020-2023  润新知