三周精通FastAPI:38 针对不同的编程语言来生成客户端

官方文档:https://fastapi.tiangolo.com/zh/advanced/generate-clients/

生成客户端¶

因为 FastAPI 是基于OpenAPI规范的,自然您可以使用许多相匹配的工具,包括自动生成API文档 (由 Swagger UI 提供)。

一个不太明显而又特别的优势是,你可以为你的API针对不同的编程语言生成客户端(有时候被叫做 SDKs )。

OpenAPI 客户端生成¶

有许多工具可以从OpenAPI生成客户端。

一个常见的工具是 OpenAPI Generator。

如果您正在开发前端,一个非常有趣的替代方案是 openapi-ts。

生成一个 TypeScript 前端客户端¶

让我们从一个简单的 FastAPI 应用开始:

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strprice: floatclass ResponseMessage(BaseModel):message: str@app.post("/items/", response_model=ResponseMessage)
async def create_item(item: Item):return {"message": "item received"}@app.get("/items/", response_model=list[Item])
async def get_items():return [{"name": "Plumbus", "price": 3},{"name": "Portal Gun", "price": 9001},]

请注意,路径操作 定义了他们所用于请求数据和回应数据的模型,所使用的模型是Item 和 ResponseMessage

API 文档¶

如果您访问API文档,您将看到它具有在请求中发送和在响应中接收数据的模式(schemas)

您可以看到这些模式,因为它们是用程序中的模型声明的。

那些信息可以在应用的 OpenAPI模式 被找到,然后显示在API文档中(通过Swagger UI)。

OpenAPI中所包含的模型里有相同的信息可以用于 生成客户端代码

生成一个TypeScript 客户端¶

现在我们有了带有模型的应用,我们可以为前端生成客户端代码。

安装 openapi-ts

您可以使用以下工具在前端代码中安装 openapi-ts:

npm install @hey-api/openapi-ts --save-dev

生成客户端代码¶

要生成客户端代码,您可以使用现在将要安装的命令行应用程序 openapi-ts

因为它安装在本地项目中,所以您可能无法直接使用此命令,但您可以将其放在 package.json文件中。它可能看起来是这样的:

{"name": "frontend-app","version": "1.0.0","description": "","main": "index.js","scripts": {"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"},"author": "","license": "","devDependencies": {"@hey-api/openapi-ts": "^0.27.38","typescript": "^4.6.2"}
}

在这里添加 NPM generate-client 脚本后,您可以使用以下命令运行它:

npm run generate-clientfrontend-app@1.0.0 generate-client /home/user/code/frontend-app
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios

此命令将在 ./src/client 中生成代码,并将在其内部使用 axios(前端HTTP库)。

尝试客户端代码¶

现在您可以导入并使用客户端代码,它可能看起来像这样,请注意,您可以为这些方法使用自动补全:

您还将自动补全要发送的数据:

Tip

请注意, name 和 price 的自动补全,是通过其在Item模型(FastAPI)中的定义实现的。

如果发送的数据字段不符,你也会看到编辑器的错误提示:

响应(response)对象也拥有自动补全:

带有标签的 FastAPI 应用¶

在许多情况下,你的FastAPI应用程序会更复杂,你可能会使用标签来分隔不同组的路径操作(path operations)。例如,您可以有一个用 items 的部分和另一个用于 users 的部分,它们可以用标签来分隔:

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strprice: floatclass ResponseMessage(BaseModel):message: strclass User(BaseModel):username: stremail: str@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):return {"message": "Item received"}@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():return [{"name": "Plumbus", "price": 3},{"name": "Portal Gun", "price": 9001},]@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):return {"message": "User received"}

生成带有标签的 TypeScript 客户端¶

如果您使用标签为FastAPI应用生成客户端,它通常也会根据标签分割客户端代码。

通过这种方式,您将能够为客户端代码进行正确地排序和分组:

在这个案例中,您有:

  • ItemsService
  • UsersService

客户端方法名称¶

现在生成的方法名像 createItemItemsPost 看起来不太简洁:

ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) 

...这是因为客户端生成器为每个 路径操作 使用OpenAPI的内部 操作 ID(operation ID)

OpenAPI要求每个操作 ID 在所有 路径操作 中都是唯一的,因此 FastAPI 使用函数名路径HTTP方法/操作来生成此操作ID,因为这样可以确保这些操作 ID 是唯一的。

但接下来我会告诉你如何改进。 🤓

自定义操作ID和更好的方法名¶

您可以修改这些操作ID的生成方式,以使其更简洁,并在客户端中具有更简洁的方法名称

在这种情况下,您必须确保每个操作ID在其他方面是唯一的。

例如,您可以确保每个路径操作都有一个标签,然后根据标签路径操作名称(函数名)来生成操作ID。

自定义生成唯一ID函数¶

FastAPI为每个路径操作使用一个唯一ID,它用于操作ID,也用于任何所需自定义模型的名称,用于请求或响应。

你可以自定义该函数。它接受一个 APIRoute 对象作为输入,并输出一个字符串。

例如,以下是一个示例,它使用第一个标签(你可能只有一个标签)和路径操作名称(函数名)。然后,你可以将这个自定义函数作为 generate_unique_id_function 参数传递给 FastAPI:

def custom_generate_unique_id(route: APIRoute):return f"{route.tags[0]}-{route.name}"app = FastAPI(generate_unique_id_function=custom_generate_unique_id)

使用自定义操作ID生成TypeScript客户端¶

现在,如果你再次生成客户端,你会发现它具有改善的方法名称:

正如你所见,现在方法名称中只包含标签和函数名,不再包含URL路径和HTTP操作的信息。

预处理用于客户端生成器的OpenAPI规范¶

生成的代码仍然存在一些重复的信息

我们已经知道该方法与 items 相关,因为它在 ItemsService 中(从标签中获取),但方法名中仍然有标签名作为前缀。😕

一般情况下对于OpenAPI,我们可能仍然希望保留它,因为这将确保操作ID是唯一的

但对于生成的客户端,我们可以在生成客户端之前修改 OpenAPI 操作ID,以使方法名称更加美观和简洁。我们可以将 OpenAPI JSON 下载到一个名为openapi.json的文件中,然后使用以下脚本删除此前缀的标签

import json
from pathlib import Pathfile_path = Path("./openapi.json")
openapi_content = json.loads(file_path.read_text())for path_data in openapi_content["paths"].values():for operation in path_data.values():tag = operation["tags"][0]operation_id = operation["operationId"]to_remove = f"{tag}-"new_operation_id = operation_id[len(to_remove) :]operation["operationId"] = new_operation_idfile_path.write_text(json.dumps(openapi_content))

通过这样做,操作ID将从类似于 items-get_items 的名称重命名为 get_items ,这样客户端生成器就可以生成更简洁的方法名称。

使用预处理的OpenAPI生成TypeScript客户端¶

现在,由于最终结果保存在文件openapi.json中,你可以修改 package.json 文件以使用此本地文件,例如:

{"name": "frontend-app","version": "1.0.0","description": "","main": "index.js","scripts": {"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"},"author": "","license": "","devDependencies": {"@hey-api/openapi-ts": "^0.27.38","typescript": "^4.6.2"}
}

生成新的客户端之后,你现在将拥有清晰的方法名称,具备自动补全错误提示等功能:

优点¶

当使用自动生成的客户端时,你将获得以下的自动补全功能:

  • 方法。
  • 请求体中的数据、查询参数等。
  • 响应数据。

你还将获得针对所有内容的错误提示。

每当你更新后端代码并重新生成前端代码时,新的路径操作将作为方法可用,旧的方法将被删除,并且其他任何更改将反映在生成的代码中。 🤓

这也意味着如果有任何更改,它将自动反映在客户端代码中。如果你构建客户端,在使用的数据上存在不匹配时,它将报错。

因此,你将在开发周期的早期检测到许多错误,而不必等待错误在生产环境中向最终用户展示,然后尝试调试问题所在。 ✨

实践

说实话,这章内容没有看懂。我原来对生成客户端的理解,是生成可以执行的软件或者app,而这里看到的是生成不同语言的sdk 。

生成不同语言的sdk可以理解,但是生成一个TypeScript的前端客户端,有点难理解。现在的理解,大约就是fastapi的docs文档,可以支持TypteScripy? 或者是说,生成sdk函数,首先当然是可以被相应的语言调用,比如TypeScript,另外,还能在编辑器里被正确识别(也就是在编辑器里能自动补齐啥的)。

生成Typescript前端客户端

源代码

将代码写入clientapi.py

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strprice: floatclass ResponseMessage(BaseModel):message: strclass User(BaseModel):username: stremail: str@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):return {"message": "Item received"}@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():return [{"name": "Plumbus", "price": 3},{"name": "Portal Gun", "price": 9001},]@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):return {"message": "User received"}

启动服务

uvicorn  clientapi:app --reload

测试服务

curl get:

curl -X 'GET' \'http://127.0.0.1:8000/items/' \-H 'accept: application/json'

返回信息:

[{"name":"Plumbus","price":3.0},{"name":"Portal Gun","price":9001.0}] 

curl post测试:

curl -X 'POST' \'http://127.0.0.1:8000/items/' \-H 'accept: application/json' \-H 'Content-Type: application/json' \-d '{"name": "tomato","price": 4.2
}'

返回信息:

{"message":"item received"}

查看openapi.json文件

这时候浏览器输入:http://127.0.0.1:8000/openapi.json

可以看到信息:

{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/items/":{"get":{"tags":["items"],"summary":"Get Items","operationId":"items-get_items","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Item"},"type":"array","title":"Response Items-Get Items"}}}}}},"post":{"tags":["items"],"summary":"Create Item","operationId":"items-create_item","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseMessage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/":{"post":{"tags":["users"],"summary":"Create User","operationId":"users-create_user","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResponseMessage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Item":{"properties":{"name":{"type":"string","title":"Name"},"price":{"type":"number","title":"Price"}},"type":"object","required":["name","price"],"title":"Item"},"ResponseMessage":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"ResponseMessage"},"User":{"properties":{"username":{"type":"string","title":"Username"},"email":{"type":"string","title":"Email"}},"type":"object","required":["username","email"],"title":"User"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}

 生成一个TypeScript 客户端¶

现在我们有了带有模型的应用,我们可以为前端生成客户端代码。

安装 openapi-ts

您可以使用以下工具在前端代码中安装 openapi-ts:

npm install @hey-api/openapi-ts --save-dev

如果npm没有,则安装npm

sudo apt install npm

如果npm慢,则设置npm加速镜像:

# 淘宝镜像,已废弃
npm config set registry https://registry.npm.taobao.org/ # 用阿里的镜像
npm config  set registry  https://registry.npmmirror.com
生成客户端代码¶

要生成客户端代码,您可以使用现在将要安装的命令行应用程序 openapi-ts

因为它安装在本地项目中,所以您可能无法直接使用此命令,但您可以将其放在 package.json文件中。它可能看起来是这样的:

{"name": "frontend-app","version": "1.0.0","description": "","main": "index.js","scripts": {"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"},"author": "","license": "","devDependencies": {"@hey-api/openapi-ts": "^0.27.38","typescript": "^4.6.2"}
}

在这里添加 NPM generate-client 脚本后,您可以使用以下命令运行它:

npm run generate-clientfrontend-app@1.0.0 generate-client /home/user/code/frontend-app
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios

输出信息:

> frontend-app@1.0.0 generate-client
> openapi-ts --input http://127.0.0.1:8000/openapi.json --output ./src/client --client axios✨ Creating Axios client
✨ Done! Your client is located in: /Users/skywalk/work/fastapi/typescript/src/client

如果有报错,则需要先安装axios:

npm install axios

生成的代码放在.src目录,比如./src/client/index.ts文件内容为:


export { ApiError } from './core/ApiError';
export { CancelablePromise, CancelError } from './core/CancelablePromise';
export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';export type { HTTPValidationError } from './models/HTTPValidationError';
export type { Item } from './models/Item';
export type { ResponseMessage } from './models/ResponseMessage';
export type { ValidationError } from './models/ValidationError';export { $HTTPValidationError } from './schemas/$HTTPValidationError';
export { $Item } from './schemas/$Item';
export { $ResponseMessage } from './schemas/$ResponseMessage';
export { $ValidationError } from './schemas/$ValidationError';export { DefaultService } from './services/DefaultService';

尽管这个TypeScript我看不懂,但是感觉很高大上!

尝试客户端代码

现在我们到生成的.src目录,创建一个index.ts文件:

import { DefaultService } from './client' ;DefaultService.createItemItemsPost

在导入'./client'里面的DefaultService后,再键入该关键字,编辑器会出自动补全: 

到了这里,基本就算完成实践任务了。

对TypeScript等前端比较熟悉的小伙伴,欢迎来指导一下啊! 

调试

npm run generate-client报错

npm run generate-client

> frontend-app@1.0.0 generate-client

> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios

🔥 Unexpected error occurred. Log saved to /Users/skywalk/work/fastapi/typescript/openapi-ts-error-1731154176311.log

🔥 Unexpected error occurred. 🚫 invalid client - select a valid client value

安装axios试试:

npm install axios

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/58958.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux【基础篇】T

--已经不知道幸福是什么味道了 Linux命令行 linux命令提示符 linux目录结构 Windows的目录结构是N个顶点,可以是C盘 可以是D盘 可以是E盘 往下。 linux的目录结构是只有一个订单 ,像一颗倒状的树木一样的。 linux常用目录含义 /etc目录下一些重要的配置…

Mesh网格

Mesh(网格) 定义:Mesh 是一个包含顶点、三角形、顶点法线、UV坐标、颜色和骨骼权重等数据的对象。它定义了3D模型的几何形状。 功能: 顶点(Vertices):构成3D模型的点。 三角形(Triangles)&…

【从零开始的LeetCode-算法】540. 有序数组中的单一元素

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。 请你找出并返回只出现一次的那个数。 你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。 示例 1: 输入: nums [1,1,2,3,3,4,4,8,8] 输出: 2示例…

传输协议设计与牧村摆动(Makimoto‘s Wave)

有一条活鱼和一条死鱼,你准备怎么做,你会将活鱼红烧或将死鱼清蒸吗?好的食材只需要最简单的烹饪,不好的食材才需要花活儿。 我此前的文字几乎都在阐述一个观点,广域网就是那条死鱼,数据中心则是那条活鱼。…

【AI学习】Mamba学习(十五):关于S4模型计算优化的再学习

前面理解了S4模型,但是对于具体的优化算法,还是没有完全理解透彻。现在补充学习。 S4 SSM HiPPO Structured Matrices 具体方案:Structured State Spaces (S4) 简单总结:用HiPPO-LegS的矩阵形式初始化SSM,解决训…

【C++】新手入门指南

> 🍃 本系列为初阶C的内容,如果感兴趣,欢迎订阅🚩 > 🎊个人主页:[小编的个人主页])小编的个人主页 > 🎀 🎉欢迎大家点赞👍收藏⭐文章 > ✌️ 🤞 &#x1…

小程序开发进阶之路-AI编程助手

之前,我独自一人开发了一个名为“心情追忆”的小程序,旨在帮助用户记录日常的心情变化及重要时刻。从项目的构思、设计、前端(小程序)开发、后端搭建到最终部署,所有环节都由我一人包办。经过一个月的努力,…

Cursor的chat与composer的使用体验分享

经过一段时间的试用,下面对 Composer 与 Chat 的使用差别进行总结: 一、长文本及程序文件处理方面 Composer 在处理长文本时表现较为稳定,可以对长文进行更改而不会出现内容丢失的情况。而 Chat 在更改长的程序文件时,有时会删除…

【笔记】自动驾驶预测与决策规划_Part6_不确定性感知的决策过程

文章目录 0. 前言1. 部分观测的马尔可夫决策过程1.1 POMDP的思想以及与MDP的联系1.1.1 MDP的过程回顾1.1.2 POMDP定义1.1.3 与MDP的联系及区别POMDP 视角MDP 视角决策次数对最优解的影响 1.2 POMDP的3种常规解法1.2.1 连续状态的“Belief MDP”方法1. 信念状态的定义2. Belief …

Latex公式转换编辑网站

https://editor.codecogs.com/ https://www.latexlive.com/home## https://simpletex.cn/ai/latex_ocr https://webdemo.myscript.com/views/math/index.html# 参考 https://latex.91maths.com/ https://web.baimiaoapp.com/image-to-latex https://blog.csdn.net/qq_45100…

注意力机制的目的:理解语义;编码器嵌入高纬空间计算;注意力得分“得到S*V”;解码器掩码和交叉注意力层用于训练;最终的编码器和输出实现大模型

目录 注意力机制的目的:理解语义中的它是小白兔 词编码器嵌入高纬空间 计算注意力得分“得到S*V” 权重QKV:连接权重 训练阶段使用解码器:翻译后的语句 解码器掩码和交叉注意力层用于训练 最终的编码器和输出实现大模型 Transformer模型中,QKV QKV的作用 举例说明…

【万字详解】如何在微信小程序的 Taro 框架中设置静态图片 assets/image 的 Base64 转换上限值

设置方法 mini 中提供了 imageUrlLoaderOption 和 postcss.url 。 其中: config.limit 和 imageUrlLoaderOption.limit 服务于 Taro 的 MiniWebpackModule.js , 值的写法要 ()KB * 1024。 config.maxSize 服务于 postcss-url 的…

【MatLab手记】 --从0到了解超超超详过程!!!

文章目录 MatLab笔记一、命令行窗口二、变量命名规则三、数据类型1. 数字2. 字符与字符串3. 矩阵3.1 矩阵创建3.2 矩阵的修改和删除3.3 矩阵的拼接与重构重排3.4 矩阵的运算方法3.5 矩阵的下标 4. 元胞数组(类似数据容器)5. 结构体 四、逻辑与流程控制五…

实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新

pages.json 中的配置 { "path": "pages/message", "style": { "navigationBarTitleText": "消息", "enablePullDownRefresh": true, "onReachBottomDistance": 50 } }, <template><view class…

IDM扩展添加到Edge浏览器

IDM扩展添加到Edge浏览器 一般情况下&#xff0c;当安装IDM软件后&#xff0c;该软件将会自动将IDM Integration Module浏览器扩展安装到Edge浏览器上&#xff0c;但在某些情况下&#xff0c;需要我们手动安装&#xff0c;以下为手动安装步骤 手动安装IDM扩展到Edge浏览器 打…

AndroidStudio-常用布局

一、线性布局LinearLayout 线性布局内部的各视图有两种排列方式: 1.orientation属性值为horizontal时&#xff0c;内部视图在水平方向从左往右排列。 2.orientation属性值为vertical时&#xff0c;内部视图在垂直方向从上往下排列。 如果不指定orientation属性&#xff0c;…

Pr 入门系列之八:使用关键帧(上)

不论是固定效果、标准效果或是第三方效果&#xff0c;都可以通过改变属性的值来达到效果控制的目的。 任何动画要表现运动或变化&#xff0c;前后至少要给出属性值的两个不同的关键状态&#xff0c;称之为“关键帧” Keyframe。 而中间状态的变化和衔接&#xff0c;则是由计算机…

万字长文解读深度学习——循环神经网络RNN、LSTM、GRU、Bi-RNN

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化深度学习——权重初始化、评估指标、梯度消失和梯度爆炸深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总万字长文解读…

qt QMovie详解

1、概述 QMovie 是 Qt 框架中用于处理动画文件的类。它支持多种动画格式&#xff0c;包括 GIF 和一些常见的视频格式&#xff08;尽管对视频格式的支持依赖于底层平台&#xff09;。QMovie 类主要用于在 QLabel 或 QGraphicsView 等控件中显示动画。通过加载动画文件&#xff…

ip addr show

本文内容来自智谱清言 ip addr show 是 Linux 系统中用于显示网络接口配置的命令。这个命令属于 iproute2 软件包&#xff0c;该软件包在大多数 Linux 发行版中都是预安装的。ip addr show 命令可以用来查看所有网络接口的当前配置&#xff0c;或者指定某个特定接口的配置。 …