尝试为具有变更前瞻性的外部应用程序设计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/...
服务端点
现在是端点本身。关注度POST
,GET
,DELETE
不是本文的主要目的,是对这些行为本身的关注。
端点可以分为名称空间和动作。每个动作还必须以支持返回类型或所需参数的基本更改的方式显示自己。
在注册用户可以发送消息的假设聊天服务中,我们可能具有以下端点:
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服务代码以支持先前版本的最佳方法是什么?”