REST端点为预见的更改进行规划的建议模式是什么


25

尝试为具有变更前瞻性的外部应用程序设计API并非易事,但预先考虑可以使以后的生活变得更轻松。我正在尝试建立一个方案,以支持将来的更改,同时通过保留先前的版本处理程序来保持向后兼容。

本文的主要关注点是对于给定产品/公司,所有定义的端点应遵循哪种模式。

基本方案

鉴于基本URL模板https://rest.product.com/我设计,所有的服务都位于下/api沿/auth和其他基于非休息终点如/doc。因此,我可以如下建立基本端点:

https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...

服务端点

现在是端点本身。关注度POSTGETDELETE不是本文的主要目的,是对这些行为本身的关注。

端点可以分为名称空间和动作。每个动作还必须以支持返回类型或所需参数的基本更改的方式显示自己。

在注册用户可以发送消息的假设聊天服务中,我们可能具有以下端点:

https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send

现在添加版本支持,以支持将来可能会中断的API更改。之后我们既可以添加版本签名/api/或之后/messages/。给定send端点,我们可以为v1提供以下内容。

https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send

所以我的第一个问题是,版本标识符的推荐位置是什么?

管理控制器代码

因此,现在我们已经确定需要支持以前的版本,因此需要以某种方式处理可能随着时间而弃用的每个新版本的代码。假设我们正在用Java编写端点,则可以通过包来管理它。

package com.product.messages.v1;
public interface MessageController {
    void send();
    Message[] list();
}

这样做的好处是,所有代码已通过命名空间分隔开,其中的任何重大更改都将意味着服务端点的新副本。这样做的不利之处在于,需要复制所有代码,并且希望对每个副本都应用/测试希望应用于新版本和先前版本的错误修正。

另一种方法是为每个端点创建处理程序。

package com.product.messages;
public class MessageServiceImpl {
    public void send(String version) {
        getMessageSender(version).send();
    }
    // Assume we have a List of senders in order of newest to oldest.
    private MessageSender getMessageSender(String version) {
        for (MessageSender s : senders) {
            if (s.supportsVersion(version)) {
                return s;
            }
        }
    }
}

现在,这将隔离版本隔离到每个端点本身,并且使错误修复与端口兼容,在大多数情况下仅需要应用一次,但这确实意味着我们需要对每个单独的端点做更多的工作才能支持此功能。

因此,我有第二个问题:“设计REST服务代码以支持先前版本的最佳方法是什么?”

Answers:


13

因此,我有第二个问题:“设计REST服务代码以支持先前版本的最佳方法是什么?”

经过精心设计的正交API可能永远不需要以向后不兼容的方式进行更改,因此,最好的方法实际上是不要拥有将来的版本。

当然,您可能不会真正获得第一次尝试。所以:

  • 按照您的计划对API进行版本控制(这是对API进行版本控制,而不是其中的单个方法),并为此带来很多麻烦。确保您的合作伙伴知道API可以更改,并且他们的应用程序应该检查以查看他们是否使用最新版本;并建议用户在有较新版本时进行升级。支持两个旧版本很困难,而支持五个旧版本则是站不住脚的。
  • 抵制每个“发行版”更新API版本的冲动。通常可以在不破坏现有客户端的情况下将新功能集成到当前版本中。它们是新功能。您想要的最后一件事是让客户忽略版本号,因为无论如何它都是向后兼容的。仅当您在不破坏现有API的情况下绝对无法前进时,才更新API版本。
  • 当需要创建一个新版本时,第一个客户端应该是先前版本的向后兼容实现。“维护API”本身应在当前API上实现。这样,您就无需维护多个完整的实现。仅当前版本,以及旧版本的多个“外壳”。对向后兼容的客户端运行现已弃用的API的回归测试,是测试新API和兼容性层的好方法。

3

第一个URI设计选项更好地表达了对整个API进行版本控制的想法。第二个可以解释为对消息本身进行版本控制。因此,这是更好的IMO:

rest.product.com/api/v1/messages/send

对于客户端库,我认为在单独的Java包中使用两个完整的实现更干净,更易于使用且更易于维护。

话虽这么说,与版本控制相比,有更好的方法来开发API。我同意您应该为此做好准备,但是我认为版本控制是不得已而为之的方法,必须谨慎使用。客户升级是一项相对较大的工作。不久前,我在博客中发表了一些想法:

http://theamiableapi.com/2011/10/18/api-design-best-practice-plan-for-evolution/

对于专门的REST API版本控制,您会发现Mark Nottingham的这篇文章很有帮助:

http://www.mnot.net/blog/2011/10/25/web_api_versioning_smackdown


3

处理API版本的另一种方法是在HTTP标头中使用Version。喜欢

POST /messages/list/{user} HTTP/1.1
Host: http://rest.service.com
Content-Type: application/json
API-Version: 1.0      <----- like here
Cache-Control: no-cache

您可以解析标头并在后端适当地处理它。

使用这种方法,客户端无需更改URL,仅更改标头即可。而且,这始终使REST端点更清洁。

如果任何客户端未发送版本标头,则可以发送400-错误请求,也可以使用API的向后兼容版本进行处理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.