三周精通FastAPI:36 OpenAPI 回调

官方文档:

OpenAPI 回调¶

您可以创建触发外部 API 请求的路径操作 API,这个外部 API 可以是别人创建的,也可以是由您自己创建的。

API 应用调用外部 API 时的流程叫做回调。因为外部开发者编写的软件发送请求至您的 API,然后您的 API 要进行回调,并把请求发送至外部 API。

此时,我们需要存档外部 API 的信息,比如应该有哪些路径操作,返回什么样的请求体,应该返回哪种响应等。

使用回调的应用¶

示例如下。

假设要开发一个创建发票的应用。

发票包括 idtitle(可选)、customertotal 等属性。

API 的用户 (外部开发者)要在您的 API 内使用 POST 请求创建一条发票记录。

(假设)您的 API 将:

  • 把发票发送至外部开发者的消费者
  • 归集现金
  • 把通知发送至 API 的用户(外部开发者)
    • 通过(从您的 API)发送 POST 请求至外部 API (即回调)来完成

常规 FastAPI 应用¶

添加回调前,首先看下常规 API 应用是什么样子。

常规 API 应用包含接收 Invoice 请求体的路径操作,还有包含回调 URL 的查询参数 callback_url。这部分代码很常规,您对绝大多数代码应该都比较熟悉了:

from typing import Unionfrom fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Invoice(BaseModel):id: strtitle: Union[str, None] = Nonecustomer: strtotal: floatclass InvoiceEvent(BaseModel):description: strpaid: boolclass InvoiceEventReceived(BaseModel):ok: boolinvoices_callback_router = APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):pass@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: Union[HttpUrl, None] = None):"""Create an invoice.This will (let's imagine) let the API user (some external developer) create aninvoice.And this path operation will:* Send the invoice to the client.* Collect the money from the client.* Send a notification back to the API user (the external developer), as a callback.* At this point is that the API will somehow send a POST request to theexternal API with the notification of the invoice event(e.g. "payment successful")."""# Send the invoice, collect the money, send the notification (the callback)return {"msg": "Invoice received"}

"提示"

callback_url 查询参数使用 Pydantic 的 URL 类型。

此处唯一比较新的内容是路径操作装饰器中的 callbacks=invoices_callback_router.routes参数,下文介绍。

存档回调¶

实际的回调代码高度依赖于您自己的 API 应用。

并且可能每个应用都各不相同。回调代码可能只有一两行,比如:

callback_url = "https://example.com/api/v1/invoices/events/"
requests.post(callback_url, json={"description": "Invoice paid", "paid": True})

但回调最重要的部分可能是,根据 API 要发送给回调请求体的数据等内容,确保您的 API 用户(外部开发者)正确地实现外部 API

因此,我们下一步要做的就是添加代码,为从 API 接收回调的外部 API存档。

这部分文档在 /docs 下的 Swagger API 文档中显示,并且会告诉外部开发者如何构建外部 API

本例没有实现回调本身(只是一行代码),只有文档部分。

"提示"

实际的回调只是 HTTP 请求。

实现回调时,要使用 HTTPX 或 Requests。

编写回调文档代码¶

应用不执行这部分代码,只是用它来记录 外部 API 。

但,您已经知道用 FastAPI 创建自动 API 文档有多简单了。

我们要使用与存档外部 API 相同的知识……通过创建外部 API 要实现的路径操作(您的 API 要调用的)。

"提示"

编写存档回调的代码时,假设您是外部开发者可能会用的上。并且您当前正在实现的是外部 API,不是您自己的 API

临时改变(为外部开发者的)视角能让您更清楚该如何放置外部 API 响应和请求体的参数与 Pydantic 模型等。

创建回调的 APIRouter

首先,新建包含一些用于回调的 APIRouter

from fastapi import APIRouter, FastAPIinvoices_callback_router = APIRouter()

创建回调路径操作

创建回调路径操作也使用之前创建的 APIRouter

它看起来和常规 FastAPI 路径操作差不多:

  • 声明要接收的请求体,例如,body: InvoiceEvent
  • 还要声明要返回的响应,例如,response_model=InvoiceEventReceived
 
class InvoiceEvent(BaseModel):description: strpaid: boolclass InvoiceEventReceived(BaseModel):ok: boolinvoices_callback_router = APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):pass

回调路径操作与常规路径操作有两点主要区别:

  • 它不需要任何实际的代码,因为应用不会调用这段代码。它只是用于存档外部 API。因此,函数的内容只需要 pass 就可以了
  • 路径可以包含 OpenAPI 3 表达式(详见下文),可以使用带参数的变量,以及发送至您的 API 的原始请求的部分

回调路径表达式¶

回调路径支持包含发送给您的 API 的原始请求的部分的  OpenAPI 3 表达式。

本例中是字符串

"{$callback_url}/invoices/{$request.body.id}" 

因此,如果您的 API 用户(外部开发者)发送请求到您的 API:

https://yourapi.com/invoices/?callback_url=https://www.external.org/events 

使用如下 JSON 请求体:

{ "id": "2expen51ve", "customer": "Mr. Richie Rich", "total": "9999" } 

然后,您的 API 就会处理发票,并在某个点之后,发送回调请求至 callback_url(外部 API):

https://www.external.org/events/invoices/2expen51ve 

JSON 请求体包含如下内容:

{ "description": "Payment celebration", "paid": true } 

它会预期外部 API 的响应包含如下 JSON 请求体:

{ "ok": true } 

"提示"

注意,回调 URL包含 callback_url (https://www.external.org/events)中的查询参数,还有 JSON 请求体内部的发票 ID(2expen51ve)。

添加回调路由¶

至此,在上文创建的回调路由里就包含了回调路径操作(外部开发者要在外部 API 中实现)。现在使用 API 路径操作装饰器的参数 callbacks,从回调路由传递属性 .routes(实际上只是路由/路径操作的列表):

@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: Union[HttpUrl, None] = None):

"提示"

注意,不能把路由本身(invoices_callback_router)传递给 callback=,要传递 invoices_callback_router.routes 中的 .routes 属性。

查看文档¶

现在,使用 Uvicorn 启动应用,打开 http://127.0.0.1:8000/docs。

就能看到文档的路径操作已经包含了回调的内容以及外部 API

实践

源代码

将代码写入callback.py文件

from typing import Unionfrom fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Invoice(BaseModel):id: strtitle: Union[str, None] = Nonecustomer: strtotal: floatclass InvoiceEvent(BaseModel):description: strpaid: boolclass InvoiceEventReceived(BaseModel):ok: boolinvoices_callback_router = APIRouter()@invoices_callback_router.post("{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):pass@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: Union[HttpUrl, None] = None):"""Create an invoice.This will (let's imagine) let the API user (some external developer) create aninvoice.And this path operation will:* Send the invoice to the client.* Collect the money from the client.* Send a notification back to the API user (the external developer), as a callback.* At this point is that the API will somehow send a POST request to theexternal API with the notification of the invoice event(e.g. "payment successful")."""# Send the invoice, collect the money, send the notification (the callback)return {"msg": "Invoice received"}

启动服务

uvicorn callback:app --reload

测试

curl命令:

curl -X 'POST' \'http://127.0.0.1:8000/invoices/?callback_url=http%3A%2F%2Fwww.quye.com' \-H 'accept: application/json' \-H 'Content-Type: application/json' \-d '{"id": "string","title": "string","customer": "string","total": 0
}'

返回信息:

{"msg":"Invoice received"}

证明回调成功。若回调失败,会给出更详细的信息:

{"detail":[{"type":"url_parsing","loc":["query","callback_url"],"msg":"Input should be a valid URL, relative URL without a base","input":"quye.com","ctx":{"error":"relative URL without a base"}}]}

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

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

相关文章

如何将现有VUE项目所有包更新到最新稳定版

更新有风险,Enter要谨慎!!! 要将项目中的所有 npm 包更新到最新稳定版,可以使用 npm-check-updates 工具。以下是具体步骤: 步骤一:安装 npm-check-updates 首先,全局安装 npm-check-updates 工具: npm install -g…

如何使用 C# 编写一个修改文件时间属性的小工具?

下面是简鹿办公一个用 C# 编写的简单工具,它可以批量修改文件的创建时间、最后访问时间和最后修改时间。我们将使用 .NET Framework 或 .NET Core 来实现这个功能。 完整示例代码 1. 创建一个新的 C# 控制台应用程序 您可以使用 Visual Studio 或 .NET CLI 创建一个…

使用PyQt5设计一个简易计算器

目录 设计UI图 代码 结果展示 设计UI图 代码 from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import QFileDialog, QMainWindow, QMessageBox from untitled import Ui_MainWindow import sysclass…

动态规划-两个数组的dp问题——712.两个字符串的最小ASCII删除和

1.题目解析 题目来源 712.两个字符串的最小ASCII删除和——力扣 测试用例 2.算法原理 1.状态表示 由于如果直接求本题会发现无从下手,不妨根据正难则反的原理,反向求公共子序列的ASCII码最大值即可,于是就转化为求公共子序列的问题&#x…

elementui中的新增弹窗在新增数据成功后再新增 发现数据无法清除解决方法

elementui中的新增弹窗在新增数据成功后再新增 发现数据无法清除解决方法 试过网上其他方法,发现表单清空数据还是有问题,索性用下面方法解决: // 给弹框里面添加 v-ifvisible测试无问题,暂时先这样解决,如果有其他方法&#x…

基于Arduino的RGB灯按键控制

一.简介 通过按键控制RGB灯分别显示7种颜色:红 、绿、 蓝、 黄、 青、 紫、 白。 二.按键控制RGB灯原理 1)RGB全彩LED: LED由三个颜色分别为:红(Red)、绿(Green)、蓝(Blue)的LED…

hive数据查询语法

思维导图 基本查询 基本语法 SELECT [ALL | DISTINCT] 字段名, 字段名, ... FROM 表名 [inner | left outer | right outer | full outer | left semi JOIN 表名 ON 关联条件 ] [WHERE 非聚合条件] [GROUP BY 分组字段名] [HAVING 聚合条件] [ORDER BY 排序字段名 asc | desc…

前端小知识:我居然没学会用 split 方法?!

小伙伴们,你们会用 JavaScript 的 split 方法吗?最近我才发现,原来我多年来一直没真正掌握它,结果在解题时被卡住了。所以今天,我决定好好整理一下这个方法的用法。 在讨论问题之前,先来看一下 split 的两种…

VTK知识学习(2)-环境搭建

1、c方案 1.1下载源码编译 官网获取源码。 利用Cmake进行项目构建。 里面要根据实际使用的情况配置相关的模块哟,这个得你自行研究下了。 CMAKEINSTALLPREFIX--这个选项的值表示VTK的安装路径,默认的路径是C:/Program Files/VTK。该选项的值可不作更…

Halcon 从XML中读取配置参数

1、XML示例 以下是一个XML配置文件的示例,该文件包含了AOI(自动光学检测)算法的环境参数和相机逻辑参数: <AOI><!--AOI算法参数 20241106--><Env><!--环境参数--><Param name="GPUName" value="NVIDIA GeForce RTX 405…

SQL--查询连续三天登录数据详解

问题&#xff1a; 现有用户登录记录表&#xff0c;请查询出用户连续三天登录的所有数据记录 id dt1 2024-04-25 1 2024-04-26 1 2024-04-27 1 2024-04-28 1 2024-04-30 1 2024-05-01 1 2024-05-02 1 2024-05-04 1 2024-05-05 2 20…

结构方程、生物群落、数据统计、绘图分析在生态领域的应用

R语言结构方程模型&#xff08;SEM&#xff09;在生态学领域中的实践应用 结构方程模型&#xff08;Sructural Equation Model&#xff09;是一种建立、估计和检验研究系统中多变量间因果关系的模型方法&#xff0c;它可以替代多元回归、因子分析、协方差分析等方法&#xff0…

vue使用canves把数字转成图片验证码

<canvas id"captchaCanvas" width"100" height"40"></canvas>function drawCaptcha(text) {const canvas document.getElementById(captchaCanvas);const ctx canvas.getContext(2d);// 设置背景颜色ctx.fillStyle #f0f0f0;ctx.f…

双指针算法习题解答

1.移动零 题目链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a;该题要求将数组中为0的元素全部转移到数组的末尾&#xff0c;同时不能改变非零元素的相对位置。 解题思路&#xff1a;我们可以用变量dest和cur将该数组分为三个区域。…

「Mac畅玩鸿蒙与硬件23」鸿蒙UI组件篇13 - 自定义组件的创建与使用

自定义组件可以帮助开发者实现复用性强、逻辑清晰的界面模块。通过自定义组件&#xff0c;鸿蒙应用能够提高代码的可维护性&#xff0c;并简化复杂布局的构建。本篇将介绍如何创建自定义组件&#xff0c;如何向组件传递数据&#xff0c;以及如何在不同页面间复用这些组件。 关键…

【SpringCloud】Nacos微服务注册中心

微服务的注册中心 注册中心可以说是微服务架构中的"通讯录"&#xff0c;它记录了服务和服务地址的映射关系 。在分布式架构中&#xff0c; 服务会注册到这里&#xff0c;当服务需要调⽤其它服务时&#xff0c;就从这里找到服务的地址&#xff0c;进行调用。 注册中心…

【Go语言】| 第1课:Golang安装+环境配置+Goland下载

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

数据库优化指南:如何将基本功能运用到极致?

一次问题 数据库的归档日志很多&#xff0c;多到那个机器的硬件不足以处理了。查看了一下为什么产生这么多日志。发现其实都是一些不当的使用方式。比如开发人员建立了一个xxxx_temp从这么名字上就应该能猜出来这是要做什么&#xff1f;美其名曰是临时表。 就是导入一批数据&am…

150道MySQL高频面试题,学完吊打面试官--关于索引的五道大厂面试题,跳槽面试很重要

前言 本专栏为150道MySQL大厂高频面试题讲解分析&#xff0c;这些面试题都是通过MySQL8.0官方文档和阿里巴巴官方手册还有一些大厂面试官提供的资料。 MySQL应用广泛&#xff0c;在多个开发语言中都处于重要地位&#xff0c;所以最好都要掌握MySQL的精华面试题&#xff0c;这也…

自攻螺钉的世纪演变:探索关键设计与应用

自攻螺钉作为现代工业和建筑中的不可或缺的标准部件&#xff0c;经过了超过100年的发展和创新。从1914年最早的铁螺钉设计到今天的自钻自攻螺钉&#xff0c;自攻螺钉的设计不断优化&#xff0c;以适应更复杂的应用需求。本文将回顾自攻螺钉的演变历程&#xff0c;分析其设计原理…