版本发现

关于 API 可发现性 的主题文档描述了 REST 服务如何暴露版本发现信息。然而,由于存在七年的历史,早于该文档的出现,因此在实际环境中存在一些非最优的设置。本文档描述了正确使用 OpenStack 版本发现的完整算法。该算法的目的是,对于完全实现 API 可发现性 指南的所有云,通过系统的路径应该是最高效的,但对于尚未…的系统,该过程会优雅地降级,最终降级到与“仅使用目录中的内容”方法行为相同。

注意

本文档包含引用处理在实际环境中遇到的一切形式的内容。在不分散其余描述的情况下,会注意指出哪种形式是首选形式,哪些形式是出于遗留原因支持的。本文档中对某种形式的提及不应被视为认可。数据的首选形式的定义可以在其他文档中找到。

版本发现算法

版本发现算法是 使用服务目录 的一部分。它的输入参数和返回值是 用户请求 中描述的输入参数和返回值的子集。目前预计 {catalog-endpoint} 已经从服务目录或直接从 {endpoint-override} 知道。

算法如下

  1. 如果用户省略了 {endpoint-version},请遵循 用户省略的 API 版本

  2. 使用 推断版本 过程从 {catalog-endpoint} 推断 {found-endpoint-version}

  3. 如果 {found-endpoint-version} 存在且 {fetch-version-information} 为 false,则停止。将 {catalog-endpoint} 作为 {service-endpoint} 返回。

  4. 如果 推断版本 过程返回错误,则 {catalog-endpoint} 不匹配 {endpoint-version}。尝试 查找文档

    注意

    如果实现了 API 可发现性 指南,则始终存在 {discovery-document}

  5. 如果无法找到 {discovery-document}{be-strict} 为 true,则停止。返回一个错误,表明版本发现失败。

  6. 确定 {single-or-multiple}{discovery-document} (参见 单个或多个版本文档)。

    注意

    如果实现了 API 可发现性 指南,{single-or-multiple} 将始终是 multiple

此时,存在四种可能性

  1. 如果 {endpoint-version}latest{single-or-multiple}single,请遵循 最新单个版本

  2. 如果 {endpoint-version}latest{single-or-multiple}multiple,请遵循 最新多个版本

  3. 如果 {endpoint-version} 是一个版本且 {single-or-multiple}single,请遵循 请求的单个版本

  4. 如果 {endpoint-version} 是一个版本且 {single-or-multiple}multiple,请遵循 请求的多个版本

用户省略的 API 版本

如果用户省略了 API 版本,则用户表示他们希望使用 {catalog-endpoint} 作为他们的 {service-endpoint}。仅运行发现以查找该端点的版本信息。

  1. {service-endpoint}{catalog-endpoint}

  2. 如果 {fetch-version-information} 为 false,则停止。从 {service-endpoint} 推断 {found-endpoint-version}。(参见 推断版本

  3. {service-endpoint} 处检索 {discovery-document}

  4. 如果找到 {discovery-document},则停止。返回其中的 {endpoint-information}(参见 返回信息)。

  5. 如果没有 {discovery-document},则尝试 查找文档

  6. 如果没有 {discovery-document},则停止。从 {service-endpoint} 推断 {found-endpoint-version}。(参见 推断版本

  7. 确定 {discovery-document}{single-or-multiple}single 还是 multiple(参见 单个或多个版本文档)。

  8. 如果 {single-or-multiple}single,则停止。返回其中的 {endpoint-information}(参见 返回信息)。

  9. 如果 {single-or-multiple}multiple,则在 {discovery-document} 中找到与 {service-endpoint} 匹配的 {endpoint-information}(参见 匹配端点)。

  10. 如果没有 {endpoint-information},则停止。从 {catalog-endpoint} 推断 {found-endpoint-version}。(参见 推断版本

  11. 停止。返回 {endpoint-information} 中的信息(参见 返回信息)。

查找文档

在某些情况下,{discovery-endpoint} 要么不会返回文档,要么不会返回我们想要的文档,因此我们需要寻找一个新的文档。

无版本文档始终优于有版本文档,因为无版本文档提供了可能的版本列表,允许发现过程一次性处理该列表并做出决策。有版本文档仅包含一个版本,因此如果其中的版本与用户的请求不匹配,则必须进行额外的调用。

查找新文档的算法如下

  1. 如果存在现有的 {discovery-document}{single-or-multiple}multiple,则停止。没有更好的文档了。

  2. 如果

    • 存在现有的 {discovery-document}

    • {single-or-multiple}single

    • 链接部分中的 collection 链接与当前的 {discovery-endpoint} 不同

    collection 链接处的端点设为新的 {discovery-endpoint} 并获取新的 {discovery-document}。停止。返回新的 {discovery-document}

  3. 从令牌中获取当前范围的 project_id(如果存在)。

  4. 如果 {discovery-endpoint} 以一个以 project_id 结尾的路径元素结尾,则删除该路径元素并将结果 URL 设为新的 {discovery-endpoint}

  5. 如果当前的 {discovery-endpoint} 以一个形式为“v[0-9]+(.[0-9]+)?$”的路径元素结尾,则删除该路径元素,但将其保存为 {removed-version-path-element}。将结果 URL 设为新的 {discovery-endpoint}

  6. 如果 {discovery-endpoint} 匹配 {catalog-endpoint},则停止。返回一个错误报告,没有可用的 {discovery-document}

  7. 尝试从 {discovery-endpoint} 获取 {discovery-document}。如果存在,则停止。对其进行规范化(参见 规范化文档)并将其作为 {dicovery-document} 返回。

  8. 如果在新的端点找不到新的 {discovery-document},但 {removed-version-path-element} 中保存了值,则将 {removed-version-path-element} 附加到 {discovery-endpoint} 并将结果 URL 设为新的 {discovery-endpoint}

  9. 尝试从 {discovery-endpoint} 获取 {discovery-document}。如果存在,则停止。对其进行规范化(参见 规范化文档)并将其作为 {dicovery-document} 返回。

  10. 如果找不到文档,则返回一个错误报告,没有可用的 {discovery-document}

例如

# Given a discovery document from the cloud
original_document = {
  "version": {
    "status": "SUPPORTED",
    "id": "v2.0",
    "links": [
      {
        "href": "http://compute.example.com/v2/",
        "rel": "self"
      },
      {
        "href": "http://compute.example.com/",
        "rel": "collection"
      }
    ]
  }
}

# It is a single version document
single_or_multiple = 'single'

# We apply the normalization process
normalized_document = {
  "versions": [
    {
      "status": "SUPPORTED",
      "id": "v2.0",
      "min_version": "",
      "max_version": "",
      "links": [
        {
          "href": "http://compute.example.com/v2/",
          "rel": "self"
        },
        {
          "href": "http://compute.example.com/",
          "rel": "collection"
        }
      ]
    }
  ]
}

# We see that a collection link exists, so we'll use it as the new discovery
# endpoint.
discovery_endpoint = "http://compute.example.com/"

# We fetch the document from that endpoint and normalize it.
normalized_better_discovery_document = {
  "versions": [
    {
      "status": "SUPPORTED",
      "links": [
        {
          "href": "http://compute.example.com/v2/",
          "rel": "self"
        }
      ],
      "min_version": "",
      "max_version": "",
      "id": "v2.0"
    }, {
      "status": "CURRENT",
      "links": [
        {
          "href": "http://compute.example.com/v2.1/",
          "rel": "self"
        }
      ],
      "min_version": "2.1",
      "max_version": "2.38",
      "id": "v2.1"
    }
  ]
}

# single-or-multiple is multiple, so it's better
return normalized_better_discovery_document

带有 project_id 的示例

# The user has requested service-type=file-storage

# The user's token reports the project_id
project_id = '45f0034e8c5a4ef4895b5a87b6b57def'
# The service-catalog contains an entry for filestorage
catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'

# The catalog_endpoint ends with the user's project_id, so we pop it.
new_endpoint = 'https://file-storage.example.com/v2'

# Fetch the document, normalize it and return it
return {
  "versions": [
    {
      "status": "CURRENT",
      "id": "v2.0",
      "links": [
        {
          "href": "http://file-storage.example.com/v2/",
          "rel": "self"
        },
        {
          "href": "http://file-storage.example.com/",
          "rel": "collection"
        }
      ]
    }
  ]
}

更病态的示例

# The user has requested service-type=file-storage

# The user's token reports the project_id
project_id = '45f0034e8c5a4ef4895b5a87b6b57def'
catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'

# The catalog_endpoint ends with the user's project_id, so we pop it.
discovery_endpoint = 'https://file-storage.example.com/v2'

# We try to fetch https://file-storage.example.com/v2 but it returns an error

# Pop version string from the endpoint
new_discovery_endpoint = 'https://file-storage.example.com/'

# Fetch the document, normalize and return it
return {
  "versions": [
    {
      "status": "SUPPORTED",
      "links": [
        {
          "href": "http://file-storage.example.com/v1/",
          "rel": "self"
        }
      ],
      "min_version": "",
      "max_version": "",
      "id": "v1.0"
    },
    {
      "status": "CURRENT",
      "links": [
        {
          "href": "http://file-storage.example.com/v2/",
          "rel": "self"
        }
      ],
      "min_version": "2.0",
      "max_version": "2.22",
      "id": "v2.0"
    }
  ]
}

推断版本

在大多数情况下,{service-endpoint} 的版本应该可以从 {discovery-document} 中检索到,并且在这些情况下,它应该被认为是 {service-endpoint} 处的服务的版本。在某些情况下,找不到与问题中的 {service-endpoint} 对应的发现文档。或者,在某些情况下,{catalog-endpoint} 包含版本信息,并且用户没有寻找微版本信息。

当使用此过程时,微版本信息将始终为空。

推断版本的算法如下

  1. 如果存在,则从令牌中获取当前范围的 project_id

  2. 如果端点以一个以 project_id 结尾的路径元素结尾,则删除它。

  3. 如果端点以形式为 ^v[0-9]+(\.[0-9]+)?$ 的路径元素结尾,则去除 v 并将剩余部分用作 {found-endpoint-version}

  4. 如果端点不包含版本元素,则无法推断版本。返回 {found-endpoint-version} 的空值。

  5. 如果提供了 {endpoint-version} 且与 {found-endpoint-version} 不匹配,则停止。返回一个错误,说明用户请求了一个版本,并且从 URL 推断的版本不匹配。

  6. 返回 {found-endpoint-version}

例如

catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'
# Match path elements - /v2/ matches ...
found_api_version = '2'

catalog_endpoint = 'https://identity-storage.example.com/'
# Match path elements - no matches
found_api_version = None

catalog_endpoint = 'https://object-store.example.com/v1/AUTH_622b11a1-5dfa-43b4-9f58-4ad3c6dbc4a0'
# Match path elements - /v1/ matches ...
found_api_version = '1'

catalog_endpoint = 'https://compute.example.com/v2.1'
# Match path elements - /v2.1/ matches ...
found_api_version = '2.1'

匹配端点

如果 {single-or-multiple}multiple 并且发现算法选择回退到目录提供的端点,则应找到一个与目录 URL 匹配的 URL,以便可以提取版本。

  1. 使用版本比较按降序对 {discovery-document} 中的端点按 id 排序。

  2. 对于列表中的每个端点,对其进行扩展(参见 扩展端点)并将其与目录端点进行比较。第一个匹配的端点是赢家。

例如

catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'

discovery_document = {
  "versions": [
    {
      "status": "CURRENT",
      "id": "v2.0",
      "links": [
        {
          "href": "http://file-storage.example.com/v2/",
          "rel": "self"
        }
      ],
    }
  ]
}

# Expand endpoint http://file-storage.example.com/v2/
expanded_endpoint = "https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def"

# expanded_endpoint matches catalog_endpoint - id v2.0 is the match

扩展端点

发现文档中的端点可以是相对路径,也可能以已知方式存在错误。在使用发现文档中的端点之前,必须对其进行扩展。算法如下

  1. 将发现文档中的端点与获取发现文档的端点连接起来。如果文档中的端点是绝对 URL,则结果应为文档中的端点保持不变。如果文档中的端点是相对路径,则应根据常规相对 URL 规则将其附加到获取文档的端点。Python 模块 six.moves.urllib.parse.urljoin 是一个行为符合预期的 URL 连接实现的示例。

  2. 替换发现文档中端点的 schemehost,使用其获取的端点的 schemehost。这是为了解决在实际环境中看到的旧版存在错误的发现文档的问题。

    例如

def replace_scheme(endpoint, discovery_url):
     parsed_endpoint = urllib.parse.urlparse(endpoint)
     parsed_discovery_url = urllib.parse.urlparse(discovery_url)

     return urllib.parse.ParseResult(
         parsed_discovery_url.scheme,
         parsed_discovery_url.netloc,
         parsed_endpoint.path,
         parsed_endpoint.params,
         parsed_endpoint.query,
         parsed_endpoint.fragment).geturl()
  1. 如果存在,则从令牌中获取当前范围的 project_id

  2. 如果 {catalog-endpoint} 结尾的路径元素以 project_id 结尾,但端点没有,则将 {catalog-endpoint} 路径的最后一个元素附加到端点的末尾。

注意

有些服务会在其端点中的 project_id 前面加上一个字符串,因此仅仅将 project_id 附加到 catalog-endpoint 并不足以解决问题。

例如

project_id = '45f0034e8c5a4ef4895b5a87b6b57def'
catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'

discovery_document = {
  "versions": [
    {
      "status": "CURRENT",
      "id": "v2.0",
      "links": [
        {
          "href": "/v2.0",
          "rel": "self"
        }
      ]
    }
  ]
}

# Pop project_id from catalog_endpoint
shortened_catalog_endpoint = 'https://file-storage.example.com/v2'

# Apply URL join to https://file-storage.example.com/v2 and /v2.0
joined_endpoint = 'https://file-storage.example.com/v2.0'

# catalog_endpoint ends with project_id, append project_id
service_endpoint = 'http://file-storage.example.com/v2.0/45f0034e8c5a4ef4895b5a87b6b57def'

使用发现中损坏的服务端点

project_id = '45f0034e8c5a4ef4895b5a87b6b57def'
catalog_endpoint = 'https://file-storage.example.com/v2/45f0034e8c5a4ef4895b5a87b6b57def'

# This discovery_document is the result of a service with a broken
# configuration. Obviously the service is not on "localhost". Similarly,
# since the discovery endpoint is an https endpoint, it can be assumed
# that the actual service endpoint is https.
discovery_document = {
  "versions": [
    {
      "status": "CURRENT",
      "id": "v2.0",
      "links": [
        {
          "href": "https:///v2.0",
          "rel": "self"
        }
      ],
    }
  ]
}

# Pop project_id from catalog_endpoint
shortened_catalog_endpoint = 'https://file-storage.example.com/v2'

# Apply URL join to https://file-storage.example.com/v2 and
# https:///v2.0 - endpoint from discovery is absolute
joined_endpoint = 'https:///v2.0'

# Replace scheme and host from https://file-storage.example.com/v2
joined_endpoint = 'https://file-storage.example.com/v2.0'

# catalog_endpoint ends with project_id, append project_id
service_endpoint = 'http://file-storage.example.com/v2.0/45f0034e8c5a4ef4895b5a87b6b57def'

单个或多个版本文档

即使将版本文档按照 标准化文档 的方式标准化为 API 可发现性 中描述的形式,仍然重要的是要知道文档是否列出了所有可用版本,或者只是更大集合中的单个版本。由于也可能只有一个版本,仅仅查看列表的长度是不够的。

注意

一旦所有服务都实现了 API 可发现性 中的所有建议,将永远不会存在包含更大集合中单个版本的文档,因此不需要此逻辑。但是,该逻辑与期望的未来状态是向上兼容的。

为了应用发现算法,必须检测文档的类型。

  • 如果文档在 links 列表中包含一个 relcollection 的链接描述,并且该链接的 hrefrelself 的链接的 href 不同,那么它就是单个版本文档。

  • 否则,它就是多个版本文档,可以依赖它包含完整的可用版本集合。

注意

TODO(mordred) 添加示例

标准化文档

注意

如果 API-SIG 在 API 可发现性 中的建议得以实施,则可以跳过本节中的所有逻辑。

除了 API 可发现性 中描述的首选形式之外,还存在三种现有的版本发现文档形式。为了合理地应用算法,应将获取的文档标准化,使其与 API 可发现性 对齐。

注意

实际上,标准化不需要在客户端或库中进行。此处描述是为了简化本文档的其他部分,并能够以正确的文档格式描述该过程。

  • 如果文档具有一个名为 versions 的键,该键包含一个名为 values 的键,则将 values 中包含的列表移动到 versions 下方。然后,该列表就是版本对象列表。

例如

{
  "versions": {
    "values": [
      {
        "status": "stable",
        "updated": "2016-10-06T00:00:00Z",
        "id": "v3.7",
        "links": [
          {
            "href": "https://auth.example.com/v3/",
            "rel": "self"
          }
        ]
      },
      {
        "status": "deprecated",
        "updated": "2016-08-04T00:00:00Z",
        "id": "v2.0",
        "links": [
          {
            "href": "https://auth.example.com/v2.0/",
            "rel": "self"
          }
        ]
      }
    ]
  }
}

变为

{
  "versions": [
    {
      "status": "stable",
      "updated": "2016-10-06T00:00:00Z",
      "id": "v3.7",
      "links": [
        {
          "href": "https://auth.example.com/v3/",
          "rel": "self"
        }
      ]
    },
    {
      "status": "deprecated",
      "updated": "2016-08-04T00:00:00Z",
      "id": "v2.0",
      "links": [
        {
          "href": "https://auth.example.com/v2.0/",
          "rel": "self"
        }
      ]
    }
  ]
}
  • 如果文档具有一个名为 id 的键,则创建一个名为 version 的键,并将所有值放在其中。

例如

{
  "status": "CURRENT",
  "id": "v2.0",
  "links": [
    {
      "href": "http://network.example.com/v2.0",
      "rel": "self"
    }
  ]
}

变为

{
  "version": {
    "status": "CURRENT",
    "id": "v2.0",
    "links": [
      {
        "href": "http://network.example.com/v2.0",
        "rel": "self"
      }
    ]
  }
}
  • 如果文档具有一个名为 version 的键(即使您刚刚创建了它),请在链接列表中查找一个 collection 链接。如果不存在,则从 self 链接中获取 href。如果 self 链接以“v[0-9]+(.[0-9]+)?$”形式的版本字符串结尾,则从端点的末尾删除该版本字符串,并将一个 collection 条目添加到 links 列表中,其中包含生成的端点。

例如

{
  "version": {
    "status": "CURRENT",
    "id": "v2.0",
    "links": [
      {
        "href": "http://network.example.com/v2.0",
        "rel": "self"
      }
    ]
  }
}

变为

{
  "version": {
    "status": "CURRENT",
    "id": "v2.0",
    "links": [
      {
        "href": "http://network.example.com/v2.0",
        "rel": "self"
      },
      {
        "href": "http://network.example.com/",
        "rel": "collection"
      }
    ]
  }
}
  • 如果文档具有一个名为 version 的键,则创建一个名为 versions 的顶级键,其中包含一个列表。将 version 的内容移动到 versions 列表中的一个字典中,并删除顶级键 version

例如

{
  "version": {
    "status": "CURRENT",
    "id": "v2.0",
    "links": [
      {
        "href": "http://network.example.com/v2.0",
        "rel": "self"
      },
      {
        "href": "http://network.example.com/",
        "rel": "collection"
      }
    ]
  }
}

变为

{
  "versions": [
    {
      "status": "CURRENT",
      "id": "v2.0",
      "links": [
        {
          "href": "http://network.example.com/v2.0",
          "rel": "self"
        },
        {
          "href": "http://network.example.com/",
          "rel": "collection"
        }
      ]
    }
  ]
}

对于 versions 列表中的每个版本对象

  1. 可以忽略或删除除 idversionmin_versionmax_versionstatuslinks 之外的键。

  2. status 字段中的值转换为大写。

  3. 如果 statusSTABLE,则将其更改为 CURRENT。(处理 keystone)

  4. 如果存在 version 字段但没有 max_version 字段,则创建一个 max_version 字段,其值为 version 字段中的值。(处理 nova、cinder、manila 和 ironic 微版本)

  5. links 键应包含一个列表,并且该列表应包含一个 rel 等于 self 的字典,并且可能包含另一个 rel 等于 collection 的字典。可以丢弃任何其他条目。

以下是一些完整的标准化示例。

原始文档

{
  "versions": [
    {
      "status": "stable",
      "updated": "2016-10-06T00:00:00Z",
      "id": "v3.7",
      "links": [
        {
          "href": "https://auth.example.com/v3/",
          "rel": "self"
        }
      ]
    },
    {
      "status": "deprecated",
      "updated": "2016-08-04T00:00:00Z",
      "id": "v2.0",
      "links": [
        {
          "href": "https://auth.example.com/v2.0/",
          "rel": "self"
        }
      ]
    }
  ]
}

变为

{
  "versions": [
    {
      "status": "CURRENT",
      "id": "v3.7",
      "links": [
        {
          "href": "https://auth.example.com/v3/",
          "rel": "self"
        }
      ]
    },
    {
      "status": "DEPRECATED",
      "id": "v2.0",
      "links": [
        {
          "href": "https://auth.example.com/v2.0/",
          "rel": "self"
        }
      ]
    }
  ]
}

原始文档

{
  "versions": [
    {
      "status": "SUPPORTED",
      "updated": "2011-01-21T11:33:21Z",
      "links": [
        {
          "href": "http://compute.example.com/v2/",
          "rel": "self"
        }
      ],
      "min_version": "",
      "version": "",
      "id": "v2.0"
    },
    {
      "status": "CURRENT",
      "updated": "2013-07-23T11:33:21Z",
      "links": [
        {
          "href": "http://compute.example.com/v2.1/",
          "rel": "self"
        }
      ],
      "min_version": "2.1",
      "version": "2.38",
      "id": "v2.1"
    }
  ]
}

变为

{
  "versions": [
    {
      "status": "SUPPORTED",
      "links": [
        {
          "href": "http://compute.example.com/v2/",
          "rel": "self"
        }
      ],
      "min_version": "",
      "max_version": "",
      "id": "v2.0"
    },
    {
      "status": "CURRENT",
      "links": [
        {
          "href": "http://compute.example.com/v2.1/",
          "rel": "self"
        }
      ],
      "min_version": "2.1",
      "max_version": "2.38",
      "id": "v2.1"
    }
  ]
}

查找匹配版本

通过将 {endpoint-version} 与描述的 id 字段进行比较,来查找一个版本列表,以找到 {candidate-endpoints} 列表(参见 比较主要版本)。

如果匹配请求的 {endpoint-version}{id} 超过一个,并且其中一个的 statusCURRENT,则应返回它。

如果匹配请求的 {endpoint-version}{id} 超过一个,并且没有一个的 statusCURRENT,则应返回最高的版本。

如果匹配请求的 {endpoint-version}{id} 超过一个,并且多个版本的 statusCURRENT,则应返回最高的版本。

最新的单个版本

{endpoint-version}latest,并且 {single-or-multiple}single

  1. 如果 {discovery-document} 中的 statusCURRENT,则停止。返回 {discovery-document} 中的 {endpoint-information}(参见 返回信息)。

  2. 尝试 查找文档

  3. 如果有一个新的 {discovery-document},则确定 {single-or-multiple}single 还是 multiple(参见 单个或多个版本文档)。

  4. 如果新的 {single-or-multiple}multiple,则遵循 最新的多个版本

  5. 如果新的 {single-or-multiple}single,或者没有新的 {discovery-document},则停止。返回 {discovery-document} 中的 {endpoint-information}(参见 返回信息)。

最新的多个版本

{endpoint-version}latest,并且 {single-or-multiple}multiple

  1. {discovery-document} 中找到具有最新版本的 {endpoint-information},参见 查找最新版本

  2. 找到 {endpoint-information} 后,停止。返回 {endpoint-information} 中的信息(参见 返回信息)。

请求的单个版本

{endpoint-version} 是一个版本或范围,并且 {single-or-multiple}single

  1. 通过遵循 查找匹配版本,检查 {discovery-document} 中的版本是否与 {endpoint-version} 匹配。

  2. {discovery-document} 中找到一个与 {endpoint-version} 匹配的 {endpoint-information}。(参见 查找匹配版本

  3. 如果找到 {endpoint-information},则停止。返回 {endpoint-information} 中的信息(参见 返回信息)。

  4. 如果版本不匹配,则尝试 查找文档

  5. 如果有一个新的 {discovery-document},则确定 {single-or-multiple}single 还是 multiple(参见 单个或多个版本文档)。

  6. 如果 {single-or-multiple}multiple,则遵循 请求的多个版本

  7. 如果没有新的 {discovery-document},则停止。返回一个错误,告诉用户他们请求的版本未找到。在错误中包含找到的版本。

请求的多个版本

{endpoint-version} 是一个版本或范围,并且 {single-or-multiple}multiple

  1. {discovery-document} 中找到一个匹配的 {endpoint-information}(参见 查找匹配版本

  2. 如果找到 {endpoint-information},则停止。返回 {endpoint-information} 中的信息(参见 返回信息)。

  3. 如果未找到匹配的 {endpoint-information} 并且 {be-strict}True,则停止。返回一个错误,告诉用户他们请求的版本未找到。在错误中包含找到的版本列表。

  4. 如果未找到匹配的 {endpoint-information} 并且 {be-strict} 是 False,则使用 {catalog-endpoint} 作为 {service-endpoint}。找到文档中与 {catalog-endpoint} 匹配的 {endpoint-information} 并使用它。(参见 匹配端点)。

  5. 如果没有 {endpoint-information},则停止。从 {service-endpoint} 推断 {found-endpoint-version}(参见 推断版本)。

  6. 停止。返回 {endpoint-information} 中的信息(参见 返回信息)。

查找最新版本

如果列表中某个版本的 statusCURRENT,则使用它。

否则,选择 id 最高的版本,排除任何 statusEXPERIMENTALDEPRECATED 的版本,并使用版本比较(而非词法排序)进行排序。

返回信息

当端点信息被选中后,以以下方式返回信息

  1. 去除 {id} 开头的 “v”,并将其作为 {found-endpoint-version} 返回。

  2. 扩展 linksrelself 的条目的 href,并将其作为 {service-endpoint} 返回(参见 扩展端点)。

  3. 如果存在,则返回 {min-version}{max-version}