反模式的API设计(一)

一个真实的反模式 API 设计案例…

最近在做一个点餐应用,使用了第三方的云打印设备,通过 http 提交订单的方式,控制打印机的打印。大致流程如下:

  1. 调用方发送打印请求到设备厂商的云后台,云后台会生成并返回一个打印任务id。
  2. 云后台讲打印任务入列,排队发送给对应的打印机。
  3. 打印机将打印结果返回云后台。
  4. 云后台讲打印机结果推送给调用方。

设备厂商的开放平台给了两个接口.

提交打印订单:

user	必须	string	飞鹅云后台注册用户名。
stime	必须	int	当前UNIX时间戳,10位,精确到秒。
backurl		string	回调地址。
expired		int	失效时间。
sig	必须	string	签名。

打印状态回调:

orderId	string	订单ID
status	int	订单状态
stime	int	UNIX时间戳
sign	string	数字签名

这里面就出现问题了:第二个接口不仅在流程上依赖第一步,在数据上也依赖第一步。

第一步在流程上看起来是个同步操作,但数据实际上依旧是异步。如果第一步没有成功获取到orderId(比如连接成功但是读取超时),但是由于云服务器已经收到了订单,因此,打印任务会照常进行,回调也会正常收到。不过,本次回调会变成一个无效的回调,因为 orderId 是无法理解的。由于接口不是幂等的,假如在这种情况下进行常规的重试,就有可能会导致同一个订单被重复打印。

所以,开放平台可以类比电商里面的“下单”与“支付”步骤,考虑几种处理方式:

  1. 在提交订单之前,增加一个“预下单”的步骤。这样一个订单就只会对应一个打印任务。
  2. 提供一个允许调用方自定义的识别码,并在回调的同时原样返回,把控制权交回给调用方,才能达成信息的一致。