- 前言
- 1、域名访问不到
- 2、签名错误
- 3、签名过期
- 4、接口突然没返回数据
- 5、token 失效
- 6、接口超时
- 7、接口返回 500
- 8、接口返回 404
- 9、接口返回少数据了
- 10、偷偷改参数了
- 11、接口时好时坏
- 12、文档和接口逻辑不一致
- 13、欠费了
前言
在实际工作中,我们经常需要在项目中调用第三方API接口,获取数据,或者上报数据,进行数据交换和通信。
那么,调用第三方 API 接口会遇到哪些问题?如何解决这些问题呢?
这篇文章就跟大家一起聊聊第三方API接口的话题,希望对你会有所帮助。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
1、域名访问不到
一般我们在第一次对接第三方平台的 API 接口时,可能会先通过浏览器或者 postman 调用一下,该接口是否可以访问。
有些人可能觉得多次一举。
其实不然。
有可能你调用第三方平台的 API 接口时,他们的接口真的挂了,他们还不知道。还有一种最重要的情况,就是你的工作网络,是否可以访问这个外网的接口。
有些公司为了安全考虑,对内网的开发环境,是设置了防火墙的,或者有一些其他的限制,有些 IP 白名单,只能访问一些指定的外网接口。
如果你发现你访问的域名,在开发环境访问不通,就要到运维同学给你添加ip白名单了。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
2、签名错误
很多第三方 API 接口为了防止别人篡改数据,通常会增加数字签名(sign)的验证。
sign = md5(多个参数拼接 + 密钥)
在刚开始对接第三方平台接口时,会遇到参数错误,签名错误等问题。
其中参数错误比较好解决,重点是签名错误这个问题。
签名是由一些算法生成的。
比如:将参数名和参数值用冒号拼接,如果有多个参数,则按首字母排序,然后再将多个参数一起拼接。然后加盐(即:密钥),再通过 md5,生成一个签名。
- 如果有多个参数,你是按首字母倒序的,则最后生成的签名会出问题。
- 如果你开发环境的密钥,用的生产环境的,也可能会导致生产的签名出现问题。
- 如果第三方平台要求最后 3 次 md5 生成签名,而你只用了 1 次,也可能会导致生产的签名出现问题。
因此,接口签名在接口联调时是比较麻烦的事情。
如果第三方平台有提供 SDK 生成签名是最好的,如果没有,就只能根据他们文档手写签名算法了。
3、签名过期
通过上面一步,我们将签名调通了,可以正常访问第三方平台获取数据了。
但你可能会发现,同一个请求,15 分钟之后,再获取数据,却返回失败了。
第三方平台在设计接口时,在签名中增加了时间戳校验,同一个请求在 15 分钟之内,允许返回数据。如果超过了 15 分钟,则直接返回“失败”。
这种设计是为了安全考虑。
防止有人利用工具进行暴力破解,不停伪造签名,不停调用接口校验,如果一直穷举下去的话,总有一天可以校验通过的。
sign = md5(多个参数拼接 + 密钥 + 时间戳)
因此,有必要增加时间戳的校验。
如果出现这种情况,不要慌,重新发起一次新的请求即可。
4、接口突然没返回数据
如果你调用第三方平台的某个 API 接口查询数据,刚开始一直都有数据返回。突然某一天没返回数据了。但是该 API 接口能够正常响应。
不要感到意外,有可能是第三方平台将数据删除了。
我对接完第三方平台的 API 接口后,部署到了测试环境,发现他们接口竟然没有返回数据,原因是他们有一天将测试环境的数据删完了。
因此,在部署测试环境之前,要先跟对方沟通,要用哪些数据测试,不能删除。
5、token 失效
有些平台的 API 接口在请求之前,先要调用另外一个 API 接口获取 token,然后在 header 中携带该 token 信息才能访问其他的业务 API 接口。
在获取 token 的 API 接口中,我们需要传入账号、密码和密钥等信息。每个接口对接方,这些信息都不一样。
我们在请求其他的 API 接口之前,每次都实时调用一次获取 token 的接口获取 token?还是请求一次 token,将其缓存到 Redis 中,后面直接从 Redis 获取数据呢?
很显然我们更倾向于后者,因为如果每次请求其他的 API 接口之前,都实时调用一次获取 token 的接口获取 token,这样每次都会请求两次接口,性能上会有一些影响。
如果将请求的 token,保存到 Redis,又会出现另外一个问题:token 失效的问题。
我们调用第三方平台获取 token 的接口获取到的 token,一般都有个有效期,比如:1 天,1 个月等。在有效期内,该 API 接口能够正常访问。如果超过了 token 的有效期,则该 API 接口不允许访问。
好办,我们把 Redis 的失效时间设置成跟 token 的有效期一样不就 OK 了?想法是不错,但是有问题。
你咋保证,你们系统的服务器时间,跟第三方平台的服务器时间一模一样?
我之前遇到过某大厂,提供了获取 token 接口,在 30 天内发起请求,每次都返回相同的 token 值。如果超过了 30 天,则返回一个新的。
有可能出现这种情况,你们系统的服务器时间要快一些,第三方平台的时间要慢一些。结果到了 30 天,你们系统调用第三方平台的获取 token 接口获取到了 token 还是老的 token,更新到 Redis 中了。
过一段时间,token 失效了,你们系统还是用老的 token 访问第三方平台的其他API接口,一直都返回失败。但获取新的 token 却要等 30 天,这个时间太漫长了。
为了解决这个问题,需要捕获 token 失效的异常。如果在调用其他的 API 接口时发现 token 失效了,马上请求一次获取 token 接口,将新的 token 立刻更新到 Redis 中。
这样基本可以解决 token 失效问题,也能尽可能保证访问其他接口的稳定性和性能。
6、接口超时
系统上线之后,调用第三方API接口,最容易出现的问题,应该是接口超时问题了。
系统到外部系统之间,有一条很复杂的链路,中间有很多环节出现问题,都可能影响 API 接口的相应时间。
作为 API 接口的调用方,面对第三方 API 接口超时问题,除了给他们反馈问题,优化接口性能之外,我们更有效的方式,可能是增加接口调用的失败重试机制。
例如:
int retryCount = 0; do { try { doPost(); break; } catch (Exception e) { log.warn("接口调用失败") retryCount++; } } where(retryCount <= 3)
如果接口调用失败,则程序会立刻自动重试 3 次。
如果重试之后成功了,则该 API 接口调用成功。
如果重试 3 次之后还是失败,则该 API 接口调用失败。
7、接口返回 500
调用第三方 API 接口,偶尔因为参数的不同,可能会出现 500 的问题。
比如:有些 API 接口对于参数校验不到位,少部分必填字段,没有校验不能为空。
此时,系统的有些请求通过某个参数去调用该 API 接口时,没有传入那个参数,对方可能会出现 NPE 问题。而该接口的返回 code,很可能是 500。
还有一种情况,就是该 API 接口的内部 bug,传入不同的参数,走了不同的条件分支逻辑,在走某个分支时,接口逻辑出现异常,可能会导致接口返回 500。
这种情况做接口重试也没用,只能联系第三方 API 接口提供者,反馈相关问题,让他们排查具体原因。
他们可能会通过修复 bug,或者修复数据,来解决这个问题。
8、接口返回 404
如果你在系统日志中发现调用的第三方API接口返回了 404,这就非常坑了。
如果第三方的 API 接口没有上线,很可能是他们把接口名改了,没有及时通知你。
这种情况,可以锤他们了。
还有一种情况是,如果第三方的 API 接口已经上线了,刚开始接口是能正常调用的。
第三方也没有改过接口地址。
后来,突然有一天发现调用第三方的 API 接口还是出现了 404 问题。
这种情况很可能是他们网关出问题了,最新的配置没有生效,或者改了网关配置导致的问题。
总之一个字:坑。
9、接口返回少数据了
之前我调过一个第三方的 API 接口分页查询数据,接入非常顺利,但后来上线之后,发现他们的接口少数据了。
一查原因发现是该分页查询接口,返回的总页数不对,比实际情况少了。
有些小伙伴可能会好奇,这么诡异的问题我是怎么发现?
之前调用第三方 API 接口分页查询分类数据,保存到我们的第三方分类表中。
突然有一天,产品反馈说,第三方有个分类在分类树中找不到。
我确认之后,发现竟然是真的没有。
从调用第三方 API 接口的响应日志中,也没有查到该分类的数据。
这个API接口是分页查询接口,目前已经分了十几页查询数据,但还是没有查到我们想要的分类。
之前的做法是先调用一次 API 接口查询第一页的数据,同时查出总页数。然后再根据总页数循环调用,查询其他页的数据。
我当时猜测,可能是他们接口返回的总页数有问题。
于是,可以将接口调用逻辑改成这样的:
- 从第一页开始,后面每调用一次 API 接口查数据,页数就加 1。然后判断接口返回的数据是否小于 pageSize,
- 如果不小于,则进行下一次调用。
- 如果小于,则说明已经是最后一页了,可以停止后续调用了。
验证之后发现这样果然可以获取那个分类的数据,只能说明第三方的分页查询接口返回的总页数比实际情况小了。
10、偷偷改参数了
我之前调用过某平台的 API 接口获取指标的状态,之前根据双方约定的状态有:正常和禁用 两种。
然后将状态更新到我们的指标表中。
后来,双方系统上线运行了好几个月。
突然有一天,用户反馈说某一条数据明明删除了,为什么在页面上还是可以查到。
此时,我查我们这边的指标表,发现状态是正常的。
然后查看调用该平台的API接口日志,发现返回的该指标的状态是:下架。
what?这是什么状态?
跟该平台的开发人员沟通后,发现他们改了状态的枚举,增加了:上架、下架等多个值,而且没有通知我们。
这就坑了。
我们这边的代码中判断,如果状态非禁用状态,都认为是正常状态。
而下架状态,自动被判断为正常状态。
经过跟对方沟通后,他们确认下架状态,是非正常状态,不应该显示指标。他们改了数据,临时解决了该指标的问题。
后来,他们按接口文档又改回了之前的状态枚举值。
11、接口时好时坏
不知道你在调用第三方接口时,有没有遇到过接口时好时坏的情况。
5 分钟前,该接口还能正常返回数据。
5 分钟后,该接口返回 503 不可用。
又过了几分钟,该接口又能正常返回数据了。
这种情况大概率是第三方平台在重启服务,在重启的过程中,可能会出现服务暂时不可用的情况。
还有另外一种情况:第三方接口部署了多个服务节点,有一部分服务节点挂了。也会导致请求第三方接口时,返回值时好时坏的情况。
此外还有一种情况:网关的配置没有及时更新,没有把已经下线的服务剔除掉。
这样用户请求经过网关时,网关转发到了已经下线的服务,导致服务不可用。网关转发请求到正常的服务,该服务能够正常返回。
如果遇到该问题,要尽快将问题反馈给第三方平台,然后增加接口失败重试机制。
12、文档和接口逻辑不一致
之前还遇到一个第三方平台提供的 API 查询接口,接口文档中明确写明了有个 dr 字段表示删除状态。
有了这个字段,我们在同步第三方平台的分类数据时,就能够知道有哪些数据是被删除的,后面可以及时调整我们这边的数据,将相关的数据也做删除处理。
后来发现有些分类,他们那边已经删除了,但是我们这边却没删除。
这是啥情况呢?
代码逻辑很简单,我 review 了一下代码,也没有 bug,为什么会出现这种情况呢?
追查日志之后发现,调用第三方平台获取分类接口时,对方并没有把已删除的分类数据返回给我们。
也就是说接口文档中的那个 dr 字段没有什么用,接口文档和接口逻辑不一致。
这个问题估计好多小伙伴都遇到过。
如果要解决这个问题,主要的方案有两种:
- 第三方平台按文档修改接口逻辑,返回删除状态。
- 我们系统在调用分类查询接口之后,根据分类 code 判断,如果数据库中有些分类的 code 不在接口返回值中,则删除这些分类。
13、欠费了
我们调用过百度的票据识别接口,可以自动识别发票信息,获取发票编号和金额等信息。
之前是另外一个同事对接的接口,后来他离职了。
发票识别功能上线,使用了很长一段时间,一直都没有出问题。
后来,某一天,生产环境用户反馈发票识别不了了。
我查询了相关服务的日志,没有发现异常,这就奇怪了。
打开代码仔细看了一下,发现那位同事的代码中调用第三方的API接口,接收响应数据时,直接转换成了对象,没有打印当时返回的字符串。
莫非,接口返回值有问题?
后来,我增加了日志,打印出了该接口真正的返回内容值。
原因一下查到了,原来是欠费了。
如果出现该异常,百度的 API 接口返回的数据结构,用之前那位同事的实体有些参数没法获取到。
这是一个不小的坑。
我们在接收第三方 API 接口返回数据时,尽可能先用字符串接收返回值,然后将字符串转换成相应实体类,一定要将该返回值在日志中打印出来,方便后面定位问题。
不要直接用实体对象接收返回值,有些 API 接口,如果出现不同的异常,返回的数据结构差异比较大。
有些异常结果可能是他们网关系统直接返回的,有些异常是他们业务系统返回的。