如何使用FastAPI返回JSON格式的数据?

2024-03-10

我在两者中编写了具有相同功能的相同 API 应用程序FastAPI and Flask。但是,当返回 JSON 时,两个框架之间的数据格式不同。两者使用相同的json库,甚至相同的代码:

import json
from google.cloud import bigquery
bigquery_client = bigquery.Client()

@router.get('/report')
async def report(request: Request):
    response = get_clicks_impression(bigquery_client, source_id)
    return response

def get_user(client, source_id):
    try:
        query = """ SELECT * FROM ....."""
        job_config = bigquery.QueryJobConfig(
            query_parameters=[
                bigquery.ScalarQueryParameter("source_id", "STRING", source_id),
            ]
        )
        query_job = client.query(query, job_config=job_config)  # Wait for the job to complete.
        result = []
        for row in query_job:
            result.append(dict(row))
        json_obj = json.dumps(result, indent=4, sort_keys=True, default=str)

    except Exception as e:
        return str(e)

    return json_obj

返回的数据在Flask是字典:


  {
    "User": "fasdf",
    "date": "2022-09-21",
    "count": 205
  },
  {
    "User": "abd",
    "date": "2022-09-27",
    "count": 100
  }
]

而在FastAPI是字符串:

"[\n    {\n        \"User\": \"aaa\",\n        \"date\": \"2022-09-26\",\n        \"count\": 840,\n]"

我使用的原因json.dumps()就是它date不能是可迭代的。


错误的做法

如果在返回对象之前序列化该对象,请使用json.dumps()(如您的示例所示),例如:

import json

@app.get('/user')
async def get_user():
    return json.dumps(some_dict, indent=4, default=str)

返回的 JSON 对象最终将被连载两次,正如 FastAPI 将自动地在幕后序列化返回值。因此,您最终得到输出字符串的原因是:

"[\n    {\n        \"User\": \"aaa\",\n        \"date\": \"2022-09-26\",\n ... 

解决方案

查看可用的解决方案,以及下面关于 FastAPI/Starlette 如何在后台工作的说明。

Option 1

第一个选项是返回数据(例如dict, list等)照常 - 即使用,例如,return some_dict——还有 FastAPI,在幕后,会自动将该返回值转换为 JSON https://fastapi.tiangolo.com/advanced/response-directly/,首先将数据转换为 JSON 兼容的数据后,使用jsonable_encoder https://fastapi.tiangolo.com/tutorial/encoder/. The jsonable_encoder ensures不可序列化的对象,例如datetime https://docs.python.org/3/library/datetime.html对象,被转换为str。然后,FastAPI 会将 JSON 兼容的数据放入JSONResponse https://fastapi.tiangolo.com/advanced/custom-response/?h=jsonresp#jsonresponse,这将返回一个application/json对客户端的编码响应(这也在选项 1 中进行了解释)这个答案 https://stackoverflow.com/a/71205127/17865804). The JSONResponse,从Starlette的源代码中可以看出here https://github.com/encode/starlette/blob/858629f5188bc79d452600b1eb90eaa0045f6454/starlette/responses.py#L181,将使用Python标准json.dumps()序列化dict(对于替代/更快的 JSON 编码器,请参阅这个答案 https://stackoverflow.com/a/73580096/17865804 and 这个答案 https://stackoverflow.com/a/74173023/17865804).

Example

from datetime import date


d = [
    {"User": "a", "date": date.today(), "count": 1},
    {"User": "b", "date": date.today(), "count": 2},
]


@app.get('/')
def main():
    return d

以上是相等的 to:

from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder

@app.get('/')
def main():
    return JSONResponse(content=jsonable_encoder(d))

Output:

[{"User":"a","date":"2022-10-21","count":1},{"User":"b","date":"2022-10-21","count":2}]


返回一个JSONResponse或自定义Response直接(在下面的选项 2 中演示),以及继承自的任何其他响应类Response(参见FastAPI的文档here https://fastapi.tiangolo.com/advanced/custom-response/,以及 Starlette 的文档here https://www.starlette.io/responses/和响应的实施here https://github.com/encode/starlette/blob/master/starlette/responses.py),还允许指定一个custom status_code,如果他们愿意的话。 FastAPI/Starlette的实现JSONResponse可以找到类here https://github.com/encode/starlette/blob/da7adf246de5495b154b45e32d6fa95e181993d8/starlette/responses.py#L185,以及可以使用的 HTTP 代码列表(而不是传递HTTP 响应状态码 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status as an int直接)可见here https://github.com/encode/starlette/blob/da7adf246de5495b154b45e32d6fa95e181993d8/starlette/status.py#L11。例子:

from fastapi import status
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder

@app.get('/')
def main():
    return JSONResponse(content=jsonable_encoder(d), status_code=status.HTTP_201_CREATED)

Option 2

如果出于任何原因(例如,尝试强制使用某些自定义 JSON 格式),您必须在返回对象之前序列化该对象,然后您可以返回自定义Response直接地 https://fastapi.tiangolo.com/advanced/response-directly/#returning-a-custom-response,如中所述这个答案 https://stackoverflow.com/a/72246260/17865804。根据文档 https://fastapi.tiangolo.com/advanced/response-directly/#notes:

当您返回一个Response直接它的数据是not已验证, 转换(序列化),也不会自动记录。

另外,如上所述here https://fastapi.tiangolo.com/advanced/custom-response/?h=jsonresp#response:

FastAPI(实际上是 Starlette)会自动包含一个 内容长度标头。它还将包括一个 Content-Type 标头, 基于media_type并附加文本类型的字符集。

因此,您还可以设置media_type无论您期望数据是什么类型;在这种情况下,即application/json。下面给出示例。

Note 1:此答案中发布的 JSON 输出(在选项 1 和选项 2 中)是直接通过浏览器访问 API 端点的结果(即,通过在浏览器的地址栏中键入 URL,然后按 Enter 键)。如果您通过 Swagger UI 测试了端点/docs相反,您会看到缩进不同(在两个选项中)。这是由于 Swagger UI 格式的原因application/json回应。如果您还需要在 Swagger UI 上强制自定义缩进,则可以避免指定media_type为了Response在下面的例子中。这将导致内容显示为text,作为Content-Type响应中会缺少标头,因此 Swagger UI 无法识别数据的类型,以便自定义它们的格式(如果是application/json反应)。

Note 2:设置default论证str in json.dumps() https://docs.python.org/3/library/json.html#json.dumps是什么使得序列化成为可能date对象,否则如果未设置,您将得到:TypeError: Object of type date is not JSON serializable. The default是一个为无法序列化的对象调用的函数。它应该返回对象的 JSON 编码版本。在这种情况下是str,这意味着每个不可序列化的对象都会转换为字符串。您还可以使用自定义函数或JSONEncoder子类,如图所示here https://stackoverflow.com/questions/11875770/how-to-overcome-datetime-datetime-not-json-serializable,如果您想以自定义方式序列化对象。此外,正如前面选项 1 中提到的,可以使用替代 JSON 编码器,例如orjson,与标准相比,这可能会提高应用程序的性能json图书馆(参见这个答案 https://stackoverflow.com/a/73580096/17865804 and 这个答案 https://stackoverflow.com/a/74173023/17865804).

Note 3:FastAPI/Starlette 的Response https://github.com/encode/starlette/blob/858629f5188bc79d452600b1eb90eaa0045f6454/starlette/responses.py#L38接受作为content论点要么str or bytes目的。如实现所示here https://github.com/encode/starlette/blob/858629f5188bc79d452600b1eb90eaa0045f6454/starlette/responses.py#L60,如果你没有通过bytes对象,Starlette 将尝试使用它进行编码content.encode(self.charset)。因此,例如,如果您通过了dict,你会得到:AttributeError: 'dict' object has no attribute 'encode'。在下面的示例中,一个 JSONstr被传递,稍后将被编码为bytes(您也可以在将其传递给Response目的)。

Example

from fastapi import Response
from datetime import date
import json


d = [
    {"User": "a", "date": date.today(), "count": 1},
    {"User": "b", "date": date.today(), "count": 2},
]


@app.get('/')
def main():
    json_str = json.dumps(d, indent=4, default=str)
    return Response(content=json_str, media_type='application/json')

Output:

[
    {
        "User": "a",
        "date": "2022-10-21",
        "count": 1
    },
    {
        "User": "b",
        "date": "2022-10-21",
        "count": 2
    }
]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用FastAPI返回JSON格式的数据? 的相关文章

  • 将 numpy 数组合并为单个 int

    numpy 数组怎么可以这样 10 22 37 45 转换为单个 int32 数字 如下所示 10223745 这可以工作 gt gt gt int join map str 10 22 37 45 10223745 基本上你使用map s
  • Mypy 无法从文字列表推断项目的类型

    我有一个变量x和一个文字列表 例如 0 1 2 我想转换x这些文字之一 如果x在列表中 我将其退回 否则我返回一个后备值 from typing import Literal Set Foo Literal 0 1 2 foos Set F
  • Matplotlib:如何有效地将大量线段着色为独立渐变

    Python 绘图库 如何有效地将大量线段着色为独立渐变 已经 阅读this https stackoverflow com questions 8500700 how to plot a gradient color line in ma
  • 如何通过 python 多处理利用所有核心

    我一直在摆弄Python的multiprocessing现在已经使用了一个多小时的功能 尝试使用并行化相当复杂的图形遍历函数multiprocessing Process and multiprocessing Manager import
  • 使用 Paramiko 进行 DSA 密钥转发?

    我正在使用 Paramiko 在远程服务器上执行 bash 脚本 在其中一些脚本中 存在与其他服务器的 ssh 连接 如果我只使用 bash 不使用 Python 我的 DSA 密钥将被第一个远程服务器上的 bash 脚本转发并使用 以连接
  • 如何在 C# 中将 Json 转换为对象

    我想将 Json 转换为 C 中的对象 这里的 Json 是 值 e920ce0f e3f5 4c6f 8e3d d2fbc51990e4 如何使用 Object 问题看似愚蠢 但其实并不那么愚蠢 我没有简单的 Json 我有 IEnume
  • Python Anaconda:如何测试更新的库是否与我现有的代码兼容?

    我在 Windows 7 机器上使用 Python 2 7 Anaconda 安装进行数据分析和科学计算 当新的库发布时 例如新版本的 pandas patsy 等 您建议我如何测试新版本与现有代码的兼容性 是否可以在同一台机器上安装两个
  • 根据其他单元格值更改多个单元格值

    我想更改包含的单元格moving to movingToOpenor movingToClose基于下一个单元格中给出的状态 有时循环会被中断并且不会从open to close or close to open 这是我当前的数据框 Dat
  • 使用 for 循环创建一系列元组

    我已经搜索过 但找不到答案 尽管我确信它已经存在了 我对 python 很陌生 但我以前用其他语言做过这种事情 我正在以行形式读取数据文件 我想将每行数据存储在它自己的元组中 以便在 for 循环之外访问 tup i inLine wher
  • 两个不同长度的数据帧的列之间的余弦相似度?

    我在 df1 中有文本列 在 df2 中有文本列 df2 的长度将与 df1 的长度不同 我想计算 df1 text 中每个条目与 df2 text 中每个条目的余弦相似度 并为每场比赛给出分数 输入样本 df1 mahesh suresh
  • 从 Flask 运行 NPM 构建

    我有一个 React 前端 我想在与我的 python 后端 API 相同的源上提供服务 我正在尝试使用 Flask 来实现此目的 但我遇到了 Flask 找不到我的静态文件的问题 我的前端构建是用生成的npm run build in s
  • Ubuntu systemd 自定义服务因 python 脚本而失败

    希望获得有关 Ubuntu 中的 systemd 守护进程服务的一些帮助 我写了一个 python 脚本来禁用 Dell XPS 上的触摸屏 这更像是一个问题 而不是一个有用的功能 该脚本可以工作 但我不想一直启动它 这就是为什么我想到编写
  • Python 中维基百科 API 中的 DisambiguationError 和 GuessedAtParserWarning

    我想获得维基百科与搜索词相关的可能且可接受的名称列表 在这种情况下是 电晕 当输入以下内容时 print wikipedia summary Corona 这给出了以下输出 home virej local lib python3 8 si
  • 在Raspberry pi上升级skimage版本

    我已经使用 Raspberry Pi 2 上的 synaptic 包管理器安装了 python 包 然而 skimage 模块版本 0 6 是 synaptic 中最新的可用版本 有人可以指导我如何将其升级到0 11 因为旧版本中缺少某些功
  • XPath:通过当前节点属性选择当前和下一个节点的文本

    首先 这是从我之前的问题 https stackoverflow com questions 5202187 xpath select current and next nodes text by current node attribut
  • 无法通过 Python 子进程进行 SSH

    我需要通过堡垒 ssh 进入机器 因此 该命令相当长 ssh i
  • AWS Lambda 不读取环境变量

    我正在编写一个 python 脚本来查询 Qualys API 中的漏洞元数据 我在 AWS 中将其作为 lambda 函数执行 我已经在控制台中设置了环境变量 但是当我执行函数时 出现以下错误 module initialization
  • 如何将带有参数的Python装饰器实现为类?

    我正在尝试实现一个接受一些参数的装饰器 通常带有参数的装饰器被实现为双重嵌套闭包 如下所示 def mydecorator param1 param2 do something with params def wrapper fn def
  • 如何获取pandas中groupby对象中的组数?

    我想知道有多少个独特的组需要执行计算 给定一个名为 groupby 的对象dfgroup 我们如何找到组的数量 简单 快速 Pandaic ngroups 较新版本的 groupby API pandas gt 0 23 提供了此 未记录的
  • IndexError - 具有匀称形状的笛卡尔 PolygonPatch

    我曾经使用 shapely 制作一个圆圈并将其绘制在之前填充的图上 这曾经工作得很好 最近 我收到索引错误 我将代码分解为最简单的操作 但它甚至无法执行最简单的循环 import descartes import shapely geome

随机推荐