
FastAPI 在 JSON 中内嵌 Base64 编码的二进制数据Pydanticbytes字段的校验与序列化全指南【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapiJSON 只能承载 UTF-8 编码的字符串无法直接存放原始字节因此当应用需要在 JSON 请求/响应里附带二进制数据图片、文件片段、加密签名等时通常先把字节编码为 Base64 字符串再放进 JSON。本文以 FastAPI 仓库中 docs_src/json_base64_bytes/tutorial001_py310.py 的完整可运行示例为核心讲解如何借助 Pydantic 的val_json_bytes与ser_json_bytes模型配置让 FastAPI 在输入时自动把 Base64 解码成bytes、在输出时自动把bytes序列化成 Base64并给出仓库测试与 OpenAPI 生成结果的验证证据。读完你将掌握一套可直接复制的JSON Base64 二进制传输方案并理解它与文件上传/下载方案的取舍边界。本文内容对应 FastAPI 官方文档 JSON with Bytes as Base64中文成文另一份同主题翻译版见 docs/ko/docs/advanced/json-base64-bytes.md。为什么需要在 JSON 里编码二进制Base64 与文件方案的选择先厘清一个核心约束JSON 规范只允许 UTF-8 编码的字符串没有原始二进制数据类型。因此想通过 JSON 传递一个 PNG 文件、一段任意字节都必须先将字节编码成文本——最常用且具备互操作标准的就是 Base64。不过把二进制塞进 JSON 并不是首选方案上传二进制应优先使用 multipart 请求文件参见教程 请求文件Request Files下发二进制应优先使用流式响应例如 自定义响应中的FileResponse。Base64 虽然能把字节编码成字符串但它会比原始二进制多占用字符——每 3 个字节会被展开为 4 个 Base64 字符开销约 1/3且还需再叠加 JSON 自身的引号与转义开销因此编码进 JSON 的传输方式在字节数上明显劣于普通文件流。只有当业务上确实必须在 JSON 内承载二进制、且无法改用文件传输时才应当使用 Base64。典型的适用场景包括需要把二进制嵌套进某个结构化业务报文、与第三方 JSON API 对接或数据本身是必须放进 JSON body 的一小段字节如签名、指纹、图标数据。一个跑得通的完整示例三种模型与三个端点仓库中随文档提供的可运行示例位于 docs_src/json_base64_bytes/tutorial001_py310.py文件名后缀py310表明其依赖 Python 3.10 语法与 Pydantic v2 的model_config写法。下面把它完整展开from fastapi import FastAPI from pydantic import BaseModel class DataInput(BaseModel): description: str data: bytes model_config {val_json_bytes: base64} class DataOutput(BaseModel): description: str data: bytes model_config {ser_json_bytes: base64} class DataInputOutput(BaseModel): description: str data: bytes model_config { val_json_bytes: base64, ser_json_bytes: base64, } app FastAPI() app.post(/data) def post_data(body: DataInput): content body.data.decode(utf-8) return {description: body.description, content: content} app.get(/data) def get_data() - DataOutput: data hello.encode(utf-8) return DataOutput(descriptionA plumbus, datadata) app.post(/data-in-out) def post_data_in_out(body: DataInputOutput) - DataInputOutput: return body可以看到三个 Pydantic 模型都声明了data: bytes字段区别只在于model_configDataInput只配置val_json_bytes用于接收校验输入DataOutput只配置ser_json_bytes用于返回序列化输出DataInputOutput同时配置两者同一模型既收又发。应用搭建好后/docsSwagger UI会自动展示接口文档。官方文档截图显示POST /data的请求体说明中data字段会明确按 Base64 编码的 bytes 呈现引导调用方提交形如aGVsbG8的字符串输入侧用val_json_bytes把 Base64 解码成bytesmodel_config {val_json_bytes: base64}的含义是当 Pydantic校验输入的 JSON 数据时遇到bytes类型的字段就把它当作 Base64 编码的字符串来解析并在校验流程中自动完成 Base64 → 原始字节的解码。你的路径函数拿到的body.data已经是真正的bytes对象无需手动调用base64.b64decode()。对POST /data发送如下请求{ description: Some data, data: aGVsbG8 }提示aGVsbG8正是字符串hello的 Base64 编码。Pydantic 会把data字段的 Base64 字符串解码为bhello于是示例端点body.data.decode(utf-8)得到hello返回{ description: Some data, content: hello }这一行为在仓库测试 tests/test_tutorial/test_json_base64_bytes/test_tutorial001.py 的test_post_data中得到了端到端验证提交SGVsbG8sIFdvcmxkIQ即Hello, World!的 Base64断言返回{description: A file, content: Hello, World!}。输出侧用ser_json_bytes把bytes序列化成 Base64model_config {ser_json_bytes: base64}作用于生成 JSON 响应的过程Pydantic 在序列化模型时会把bytes字段编码为 Base64 字符串后再写入 JSON调用方拿到的是可安全放入 JSON 的文本。示例中GET /data的返回类型注解为DataOutput端点内部构造了DataOutput(descriptionA plumbus, databhello)经序列化后实际响应为{ description: A plumbus, data: aGVsbG8 }仓库测试中的test_get_data恰好断言了这一结果注意这里的data是 Base64 编码的aGVsbG8而不是明文hello说明输出配置确实生效。同一模型兼顾输入与输出如果同一个模型既要接收 Base64 编码的 JSON 输入、又要以 Base64 输出 JSON只需在model_config里同时写入两个配置项即上面的DataInputOutput。POST /data-in-out端点直接回显收到的 body{ description: A plumbus, data: SGVsbG8sIFdvcmxkIQ }由于输入经val_json_bytes解码为bytes、输出又经ser_json_bytes重新编码为 Base64响应中的data与请求保持一致实现收到什么、返回什么的透明回显。test_post_data_in_out验证的正是这种往返一致性。双端一致的 OpenAPI 文档与机器可读描述把bytes字段交给 Pydantic 后FastAPI 生成的 OpenAPI 也会随之变化。上述三个模型DataInput、DataOutput、DataInputOutput在/openapi.json中的data字段都被描述为见仓库测试中的 OpenAPI snapshot 断言{ type: string, contentEncoding: base64, contentMediaType: application/octet-stream, title: Data }也就是说OpenAPI 3.1 会通过contentEncoding: base64与contentMediaType: application/octet-stream标准字段向 Swagger UI、代码生成器等下游工具精确声明该字段承载的是 Base64 编码的字节流——这正是 Swagger UI 能向用户提示 Base64 输入样例、以及自动生成的客户端能正确编码二进制数据的原因也让 API 契约具备机器可读的准确语义。如何运行与验证基于仓库现有测试该示例依赖 Python 3.10测试用needs_py310标记门控参见 tests/utils.py与 Pydantic v2 的model_config语法。可以直接用仓库的测试来验证全部行为pytest tests/test_tutorial/test_json_base64_bytes/test_tutorial001.py -vtest_tutorial001.py内部通过importlib动态加载 docs_src/json_base64_bytes/tutorial001_py310.py并使用 FastAPI 的TestClient依次覆盖四条路径test_post_dataBase64 请求输入 → 服务端解码出可读文本test_get_databytes返回值 → Base64 序列化输出test_post_data_in_out同一模型完成解码与再编码的往返回显test_openapi_schema断言/openapi.json中三个模型及data字段的contentEncoding: base64等元数据。若想本地手动体验先安装fastapi与uvicorn然后以该示例文件为入口启动pip install fastapi uvicorn uvicorn docs_src.json_base64_bytes.tutorial001_py310:app --reload随后打开http://127.0.0.1:8000/docs即可在 Swagger UI 上直观看到三个端点的 Base64 请求体提示或直接访问http://127.0.0.1:8000/openapi.json查看生成的机器可读契约。小结与适用边界在 FastAPI 中把二进制数据放进 JSON正确姿势是声明bytes类型的 Pydantic 字段并在model_config中按需启用val_json_bytes/ser_json_bytes为base64——输入侧自动解码、输出侧自动编码、两端共用同一模型也完全支持OpenAPI 文档与客户端生成都会随之获得标准化的 Base64 语义。但请始终记得文档反复强调的取舍Base64 会让二进制膨胀约 1/3 且无法利用流式传输在传输大文件类二进制时应优先考虑 Request Files 与 FileResponse只有确实需要在 JSON 结构化报文内部携带小段二进制、且无法改用文件时这一方案才是更优解。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考