1. 接口新增入参字段,并且有校验逻辑
在日常开发中,经常会遇到的需求就是,在老的接口上,新增入参,并且需要校验。
这时候兼容性如何处理呢?我举个简单点的例子:
比如一个用户注册接口,突然加一个email的字段并且不能为空,且要校验是否符合邮箱格式
其实可以升级API版本,比如创建一个新的API版本,比如/v2/api/register,在这个新版本中包含email字段,并对其进行非空校验。
如果觉得每次新增个字段和简单校验这种情况,都要升级版本号,比较复杂繁琐,则可以将新字段email设置为可选。
如果客户端传了email字段,就对它校验,不传的话,直接放过,然后可以打印一下日志。
if (StringUtils.isNotBlank(userRequest.getEmail())) {checkEmail(userRequest.getEmail());
}else {log.info("old system request,email is null,request:{}", userRequest);
}
为什么要这么做呢?
举例,如果修改了接口后,还是有老的请求,不带email字段过来,站在业务角度,就是可以正常注册,如果直接校验拦截,那就有问题~~~
2. 接口错误码调整
有时可能需要对接口的错误码进行调整。如果调整了错误码,要及时更新接口文档,并通知上下游。说明错误码调整的原因。并且要做兼容性联调测试。
比如分布式锁获取失败的错误码,一开始是423000,上游依赖这个错误码来重试的。后来做分布式锁升级优化的时候,反手把它修改为666000了。如果不通知上下游,联调测试啥的,偷偷改就不管了。那后面肯定出问题,因为上游获取锁失败,没得重试了,依赖的错误码变更了。。。
3. 接口返回值调整了
其是不管是错误码调整还是返回值调整,都是有共性的。
要按照三步曲去走:
- 更新文档,说明修改了哪些内容
- 通知上下游,说明原因,场景
- 兼容性联调测试
比如接口不是联机交易,而是MQ交互的。
假如作为MQ的生产者,原来的消息体有个json格式的报文,然后在本次需求调整了内容。不及时做好这3步,消费端可能因为发版本后,消费不了这个报文(比如解析不了某个特殊返回值)。
4. 接口参数新增、或者修改了校验
如果接口,原来有个字段,比如一开始最大长度是10,然后后来产品需求优化,可以增加到20。然后觉得这个需求很简单,开开心心,花十分钟改完了,把入参校验扩大位数,把数据库的字段长度修改。
后来上线的时候,下游文件核对系统跑失败了。。。因为也会对这个字段进行最大长度是10的校验。
所以做这类型需求的时候,要修改参数校验逻辑的时候。一定要考虑:兼容性。上下游是否会有影响等等。
5. 接口删除,真的没请求访问了吗
有些时候,如果发现代码没引用,就会删掉。如果是对外接口的话,不能随手就删掉,要确认是否还有调用。
删除一个对外接口,要做这几件事:
- 跟上游确认,是否还有调用
- 查看监控,日志确认最近一年或者说一个月是否还有调用
- 跟产品对齐
- 说明接口作用,为什么要删除,是否影响等等
6. 接口内部,抽取公用方法,要考虑兼容性
在抽取公用方法的时候,要考虑兼容性,不能破坏原来的逻辑。
假设有一个订单处理系统,该系统支持多种支付方式(如信用卡支付、支付宝支付等)。每个支付方式有自己的处理逻辑,但现在需要添加日志记录和异常处理,并确保这些修改不会破坏现有的支付方式。
public class OrderProcessor { public void processCreditCardPayment(Order order) {
// 信用卡支付逻辑System.out.println("Processing credit card payment for order: " + order.getId());
// 假设这里有复杂的支付逻辑
} public void processAlipayPayment(Order order) {
// 支付宝支付逻辑System.out.println("Processing Alipay payment for order: " + order.getId());
// 假设这里有复杂的支付逻辑
}
}
可以抽取一个公用方法来处理日志记录和异常处理,同时保持每种支付方式的特定逻辑不变。
public class OrderProcessor {// 公用方法,处理日志记录和异常private void processPaymentWithLoggingAndExceptionHandling(Order order, Runnable paymentAction) {try {// 记录日志System.out.println("Starting payment process for order: " + order.getId());// 执行支付逻辑
paymentAction.run();// 记录支付成功日志System.out.println("Payment for order " + order.getId() + " was successful.");} catch (Exception e) {// 记录支付失败日志System.err.println("Payment for order " + order.getId() + " failed: " + e.getMessage());// 这里可以添加更多的错误处理逻辑,如重试机制或通知相关人员
}}public void processCreditCardPayment(Order order) {processPaymentWithLoggingAndExceptionHandling(order, () -> {// 信用卡支付逻辑System.out.println("Processing credit card payment for order: " + order.getId());// 假设这里有复杂的支付逻辑
});}public void processAlipayPayment(Order order) {processPaymentWithLoggingAndExceptionHandling(order, () -> {// 支付宝支付逻辑System.out.println("Processing Alipay payment for order: " + order.getId());// 假设这里有复杂的支付逻辑
});}
}
简单点描述,抽取公用方法是好事,但是不能为了公用用法,而去动到原来的逻辑,一定要做好兼容性测试。
以前一位同事,抽了一个公用方法,本来有A和B两个url入口,调用进来的,然后抽了公用方法,测试也只是校验了B方法,因为B才是本次需求改动,但是因为抽公用方法,影响了A接口导致出问题。
7. 接口新增入参、出参
平时做需求时,这个太常见了。新增入参字段,或者新增字段返回。入参、出参新增,这里还是做好三步曲:
- 更新文档,说明修改了哪些内容
- 通知上下游,说明原因,场景
- 分析兼容性影响,联调测试
8. 修改老接口时,思考接口的兼容性
很多bug都是因为修改了对外老接口,但是却不做兼容导致的。关键这个问题多数是比较严重的,可能直接导致系统发版失败的。
所以,如果需求是在原来接口上修改,尤其这个接口是对外提供服务的话,一定要考虑接口兼容。举个例子吧,比如dubbo接口,原本是只接收A,B参数,现在加了一个参数C,就可以考虑这样处理。
//老接口
void oldService(A,B);{
//兼容新接口,传个null代替C
newService(A,B,null);
}//新接口,暂时不能删掉老接口,需要做兼容。
void newService(A,B,C);
总结:
- 更新文档,说明修改了哪些内容
- 通知上下游,说明原因,场景
- 分析兼容性影响,联调测试