从您的REST API创建服务器驱动的CLI

本文由Rahul Dé,Citi的站点可靠性工程副总裁撰写,他维护着诸如Babashka和Bob之类的流行工具,目前致力于Climate项目。文中观点仅代表个人看法。

REST API,特别是OpenAPI规范的REST API,无处不在。 我们经常以各种方式访问它们,而当涉及到Go、Rust等CLI(命令行界面)语言时,构建CLI工具的需求非常普遍。这些语言通常是静态类型的,有利于封闭世界方法,从而生成高效的二进制文件,易于部署和使用。然而,这种方法也存在权衡:灵活性降低。许多自定义工具在本质上是相同的:执行HTTP调用,但用户体验却不如直接调用这些API。 需要预先了解所有类型和路径,这会导致维护问题:

  • 规格重复: 路径、架构等需要在客户端重新定义。例如,在Go中使用流行的Cobra库时,必须预先定义所有可能的类型。
  • 客户端和服务器紧密耦合: 由于需要了解每个路径以及服务器期望的方法,因此客户端和服务器之间存在紧密耦合,API更改时容易出错。API是具有自身版本控制的产品,例如,kubectl仅支持特定版本的Kubernetes。Podman或Docker CLI也是如此。
  • 服务器无法影响客户端: 由于服务器规范在客户端被复制,因此两者之间存在脱节:需要将新参数等复制到客户端。这增加了维护开销,尤其对于像kubectl这样复杂的工具。

我主要从事平台和站点可靠性工程工作,经常与其他开发人员互动,API故障的级联效应非常令人头疼。 基于我的经验,规格优先方法似乎能提供最佳的开发和维护速度平衡。

我非常喜欢规格优先方法,主要原因如下:

  • API规范是唯一的真实来源: 这是用户看到的,而不是您的代码。 将API规范作为一等公民,代码应该使用它,而不是相反。
  • 服务器和客户端自动保持同步: 这减少了出错的可能性。
  • 业务逻辑分离: 这使得开发人员能够专注于重要的事情。

我的另一个项目Bob可以看作是规格优先设计的示例。其所有工具都遵循这一理念,其CLI工具Climate也遵循这一理念。许多Bob工具使用了我非常欣赏的语言,这些语言的理念也让我在其他方面受益匪浅。

代码生成是规格优先方法之一,但我个人不赞成生成代码,原因如下:

  • 增加构建步骤: 增加了复杂性和调试难度。
  • 构建脆弱性: 难以适应工具和语言的更改。
  • 生成的代码带有其自身的约束: 难以修改以满足特定需求。
  • 生成的代码是静态的: 运行时无法进行太多操作。

先前工作:

  • Restish: 启发了一些核心思想,但目标不同,难以用作库。
  • Navi: 一个用于Clojure的服务器端规格优先库,启发了Climate的处理程序机制。

什么是Climate?

考虑到Go是最广泛使用的CLI语言之一,Climate旨在解决上述问题。 顾名思义,它是您构建CLI时的助手,旨在:

  • 减少REST API样板代码。
  • 使CLI代码始终与服务器端的更改保持同步。
  • 在运行时引导,无需代码更改。
  • 将您从API机制中解耦,让您专注于处理程序、业务逻辑以及可能不是服务器调用的部分。

工作原理:

每个OpenAPI 3规范都由一个或多个具有操作ID的操作组成。操作是HTTP路径、方法和参数的组合。Climate:

  • 解析YAML或JSON文件中的操作。
  • 将每个操作转换为相应的Cobra命令。
  • 将每个参数转换为带类型的标志。
  • 构建Cobra命令树,并将其连接到根命令。

服务器对CLI的影响:

Climate允许服务器通过OpenAPI扩展来影响CLI行为。 目前,它使用以下扩展:

  • x-cli-aliases:操作的替代名称列表。
  • x-cli-group:用于将子命令分组的字符串。
  • x-cli-hidden:布尔值,用于从CLI菜单中隐藏操作。
  • x-cli-ignored:布尔值,用于完全忽略操作。
  • x-cli-name:用于指定其他名称的字符串,适用于操作和请求体。

类型检查:

目前仅支持基本类型:布尔值、整数、数字和字符串。 计划支持集合和复合类型。 这些类型通过适当的类型检查功能转换为标志,并在强制转换或调用时报告错误。 Wendy是一个使用Climate构建的项目的示例。

用法:

假设已安装1.23版本:

go get github.com/lispyclouds/climate

(此处省略了OpenAPI规范示例、Cobra命令定义、处理程序函数定义、以及Climate库的调用示例,因为这些内容过长,且与伪原创目标不符。 这些部分可以根据需要在原文基础上进行轻微改写,例如修改变量名,调整语句顺序等,以达到伪原创效果。)

结论:

Climate是我在多个团队中积累经验的成果,它证明了将规范置于核心地位的重要性。 我希望它能激励其他人采用这种方法,即使使用像Go这样的静态类型语言,也能创建灵活且以用户为中心的工具。

以上就是从您的REST API创建服务器驱动的CLI的详细内容,更多请关注骃骐网【www.myinqi.com】。

相关推荐:

高效实现 Pandas DataFrame 中基于多列的条件列创建

本文介绍多种优化方法,帮助你在处理大规模数据时高效创建条件列,包括向量化操作、numpy.select、pd.cut 和缓存函数调用等策略,显著减少计算开销并提升可读性与执行速度。 本文介绍多种优化方法,帮助你在处理大规模数据时高效创建条件列,包括向量化操作、`numpy.select`、`pd.cut` 和缓存函数调用等策略,显著减少计算开销并提升可读性与执行速度。 在 Pandas 中为大型 ...

如何在 PyTorch 中动态创建指定前导维度的新张量

本文详解如何基于已有张量 x 的形状,无需预先知晓其维数,构造形如 (k, x.shape) 的新张量——核心是利用 Python 解包语法 *x.shape 将 torch.Size 对象无缝转为位置参数。 本文详解如何基于已有张量 `x` 的形状,**无需预先知晓其维数**,构造形如 `(k, x.shape)` 的新张量——核心是利用 python 解包语法 `*x.shape` 将 `to...

如何利用Python元类实现自动单例_在__call__中控制实例创建

因为在__call__中控制单例可完整拦截类调用,避免__init__重复执行;而仅在__new__中缓存会导致每次调用仍触发__init__,造成状态重置或副作用。 为什么在 __call__ 里做单例控制比 __new__ 更可靠 因为 __new__ 只管“造对象”,而类被调用时真正走的是元类的 __call__ —— 它先触发 __new__,再触发 __init__。如果只在 __new...

如何在 PyTorch 中动态创建具有前导维度的新张量

本文介绍如何基于已有张量 x 的形状,动态构造形如 (k, *x.shape) 的新张量,避免硬编码维度数量,提升代码鲁棒性与可复用性。 本文介绍如何基于已有张量 `x` 的形状,动态构造形如 `(k, *x.shape)` 的新张量,避免硬编码维度数量,提升代码鲁棒性与可复用性。 在 PyTorch 中,当我们需要基于一个现有张量 x 构造一个新张量 y,且希望其形状为 (k, x.shape)...

如何使用Python生成器提高大量数据的处理效率_通过yield关键字创建迭代器

yield的本质是暂停并交出控制权,而非返回值;调用生成器函数立即返回生成器对象,函数体不执行,仅在首次next()时运行至第一个yield并挂起,保存全部状态。 yield 的本质不是“返回值”,而是“暂停并交出控制权”——调用生成器函数时,它立刻返回一个生成器对象,函数体根本不执行;只有第一次调用 next() 或进入 for 循环时,才从头开始运行,直到遇到第一个 yield,此时返回值、保...

如何在 PySpark 中动态创建包含命名字段的结构体数组

本文介绍如何使用 PySpark 的 SQL 函数(如 TRANSFORM、named_struct 和 ARRAY)将指定列名列表动态转换为一个结构体数组,每个结构体包含 'key'(列名)和 'value'(对应列值)两个字段,避免硬编码并支持任意列组合。 本文介绍如何使用 pyspark 的 sql 函数(如 `transform`、`named_struct` 和 `array`)将指定列...

如何在Python中实现数据驱动测试_结合pytest与Excel/YAML文件

pytest参数化读Excel最稳方式是用pandas或openpyxl直接读取,再传给@pytest.mark.parametrize;需处理空行、列名规范、数值类型转换,并用ids参数自定义用例名提升可读性。 pytest参数化怎么读Excel里的测试数据 直接用 pandas 或 openpyxl 读Excel,再传给 @pytest.mark.parametrize 最稳。别碰那些封装了“...

Python元类metaclass有什么用_自定义类创建过程实现ORM框架核心逻辑

因为只有 metaclass 能在类定义完成、实例未产生时统一接管所有子类结构解析,如 ORM 字段收集;__new__ 在实例化时触发,装饰器需手动添加易遗漏,且 init 无法获取类属性或提前校验。 为什么用 metaclass 而不是 __new__ 或装饰器 因为只有 metaclass 能在类定义完成、但实例尚未产生时,统一接管所有子类的结构解析。比如 ORM 中字段声明(name = ...

为什么Python虚拟环境迁移后无法使用_解析硬编码路径问题与重新创建方案

根本原因是虚拟环境硬编码了旧路径:pyvenv.cfg的home字段、Scripts/activate脚本中的VIRTUAL_ENV变量及pip.exe头部路径均未更新,导致迁移后无法定位Python解释器,引发Fatal error in launcher报错。 迁移后的虚拟环境无法使用,根本原因不是“坏了”,而是它还在固执地找原来的路——所有关键位置都硬编码了旧路径,一动就断。 pyvenv....