虽然我们力求将OpenStack API设计得尽可能易于使用,但适用于各种编程语言的SDK对于开发者而言,始终是使用体验中重要的一部分。本文档包含了关于如何在针对OpenStack的SDK(软件开发工具包)中处理微版本的建议。
本文档识别出我们通常称为SDK的两种交付物。它们在向消费者公开微版本方面,推荐的方法将有所不同。
注意
如有疑问,您应该编写一个高级SDK。使用SDK的好处在于以一种适合编程语言和任何所用框架的方式来使用API。像微版本这样的东西对于不专注于API设计的开发者来说,可能看起来很陌生和令人困惑。
本文档中使用的概念
API版本,定义见:doc:microversion_specification。为简化起见,本指南将版本作为微版本的同义词。
注意
当在您的SDK中使用微版本时,请注意避免与语义版本控制产生关联。微版本与补丁版本不同,甚至在语义版本控制的意义上可以是主要版本。
在:doc:microversion_specification的意义上并非真正的API版本,而是API的一个独立世代,与同一HTTP端点树中的其他世代共存。
主要版本通过URL中的/v<数字>部分进行区分,并且是微版本的第一个组成部分。例如,在微版本1.42, 1中,
注意
是主要版本。我们似乎没有为第二个组成部分设定一个既定的名称。
由于主要版本可能实质性地改变API的结构,包括改变微版本化的机制本身,因此SDK通常应尽量保持在所请求的主要版本范围内(如果有)。
注意
我们将在所有示例中使用Python编程语言,但这些建议适用于任何编程语言,包括静态编译语言。这里的示例将使用一个虚构的“猫即服务”API及其python-catsclientSDK。
通常,SDK不应将底层API微版本暴露给用户。输入和输出数据的结构不应依赖于所使用的微版本。应采用特定于编程语言和/或所用数据格式的方法来指示某些功能和行为的存在或缺失。
例如,在当前微版本中缺失的字段,可以在Python中表示为无值,null值在Java中,或者其类型可以是Option<实际数据类型>在Rust中。
import catsclient sdk = catsclient.SDK() cat = sdk.get_cat('fluffy') if cat.color is None: print("Cat colors are not supported by this cat server") else: print("The cat is", cat.color)在此示例中,SDK在get_cat调用期间协商可以返回尽可能多信息的API微版本。如果结果版本不包含颜色字段,则将其设置为无.
。SDK应协商能更好地满足消费者需求的最高微版本。但是,它绝不应协商超出其编写和测试范围的微版本,以避免未来API更改时出现令人困惑的破坏。不言而喻,SDK不应在服务器返回的任何微版本上崩溃或表现出未定义的行为。任何不兼容性都应尽快以给定编程语言的自然形式表达。
例如,当调用一个SDK和服务器都支持的任何微版本中都无法表达的方法时,Python SDK应该抛出异常
import catsclient sdk = catsclient.SDK() cat = sdk.get_cat('fluffy') try: cat.bark() except catsclient.UnsupportedFeature: cat.meow()允许在使用前检测支持的功能也很有用
import catsclient sdk = catsclient.SDK() cat = sdk.get_cat('fluffy') if cat.can_bark(): cat.bark() else: cat.meow()在这个例子中,can_bark使用协商的微版本来检查bark调用是否能够工作。
注意
如果可能,SDK应告知消费者所需的API微版本以及为何无法使用。这可能是微版本可以并且应该泄露给消费者的唯一地方。
如果可能,主要版本应以相同方式处理,不应向用户公开。如果不可能,SDK应从可用版本中选择最新的主要版本。
低级SDK,本质上只是API的语言绑定,它紧密遵循底层API。因此,它必须向消费者公开微版本,并且必须以最接近API处理方式的方式来完成。我们建议所有调用都接受一个显式的API微版本,该版本直接发送到底层API。如果没有提供,则不应发送任何版本
import catsclient
client = catsclient.v1.get_client()
cat = client.get_cat('fluffy') # executed with no explicit version
try:
cat.bark(api_version='1.42') # executed with 1.42
except catsclient.IncompatibleApiVersion:
# no support for 1.42, falling back to older behavior
cat.meow() # executed with no explicit version
注意
在某些编程语言中,特别是那些函数没有默认参数的语言,为所有调用添加版本参数可能不便。可以使用其他方法来实现相同的结果,例如临时上下文对象
import catsclient
client = catsclient.v1.get_client()
cat = client.get_cat('fluffy') # executed with no explicit version
with cat.use_api_version('1.42') as new_cat:
new_cat.bark() # executed with 1.42
低级SDK应该明确其正在使用哪个主要版本。可以通过命名空间API或接受显式主要版本作为参数来实现。首选方法取决于API主要版本之间的差异程度。
以Python为例,要么
import catsclient
client = catsclient.v1.get_client()
或者
import catsclient
client = catsclient.get_client(1)
强烈建议提供一种查询服务器以获取支持版本范围的方法
import catsclient
client = catsclient.v1.get_client()
min_version, max_version = client.supported_api_versions()
cat = client.get_cat('fluffy') # executed with no explicit version
if max_version >= (1, 42):
cat.bark(api_version='1.42') # executed with 1.42
else:
# no support for 1.42, falling back to older behavior
cat.meow() # executed with no explicit version
应用程序通常有一个它们能够使用的基本最小API版本。建议提供一种方法来接受此类版本,并在未提供明确版本时将其用作默认值
import catsclient
try:
client = catsclient.v1.get_client(api_version='1.2')
except catsclient.IncompatibleApiVersion:
sys.exit("Cat API version 1.2 is not supported")
cat = client.get_cat('fluffy') # executed with version 1.2
try:
cat.bark(api_version='1.42') # executed with 1.42
except catsclient.IncompatibleApiVersion:
# no support for 1.42, falling back to older behavior
cat.meow() # executed with version 1.2
如本例所示,采用此方法的SDK必须提供一种清晰的方式来指出请求的版本不受支持,并尽快完成。
作为简化扩展,语言绑定可以接受一个版本列表作为基本版本。服务器支持的最高版本必须被选中并用作默认值。
import catsclient
try:
client = catsclient.v1.get_client(api_version=['1.0', '1.42'])
except catsclient.IncompatibleApiVersion:
sys.exit("Neither Cat API 1.0 nor 1.42 is supported")
cat = client.get_cat('fluffy') # executed with either 1.0 or 1.42
# whichever is available
if client.current_api_version == (1, 42):
# Here we know that the negotiated version is 1.42
cat.bark() # executes with 1.42
else:
# Here we know that the negotiated version is 1.0
cat.meow() # executes with 1.0
# The default version can still be overwritten
try:
cat.drink(catsclient.MILK, api_version='1.66') # executed with 1.66
except catsclient.IncompatibleApiVersion:
# no support for 1.66, falling back to older behavior
cat.drink() # executed with either 1.0 or 1.42 whichever is available