如何设计API接口

   实际工作中,我们经常需要和第三方平台接口打交道,那你知道一个优秀的API接口应该是什么样的吗?他应该满足安全性,可重复调用,稳定性,好定位问题等多方面要求。

签名

what:用于确保数据完整性、安全性的机制,防止数据传输过程中被篡改或伪造
how:

  1. 接口请求方将请求参数+时间戳+密钥=字符串,然后通过hash算法生成一个sign。
  2. 然后再请求参数或者请求头中增加sign参数,传递给API接口
  3. API的网关服务,获取到sign值,然后用相同的请求参数+时间戳+密钥=字符串,用相同的md5算法,算出sign值
  4. 比较两个sign值,相同则把请求转发给相应的业务,不同则直接返回签名错误。

问题:

  • 为什么添加时间戳?
    为了安全性考虑,防止同一次请求被反复利用,我们对每次请求设置一个合理的过期时间,比如10分钟。
  • 生成密钥的方式有几种?分别是什么?
    一种是双方约定一个固定值privateKey;一种是API接口提供方给出AK/SK两个值,双方约定用SK作为签名中的密钥,AK接口调用方作为 header中的accessKey传递给API接口提供方,API接口提供方可以根据AK获取SK,从而生成sign

加密

why:有时,API接口直接传递非常重要的数据 ,比如,用户的银行卡号,身份证号等,如果直接将这些参数直接明文暴露到公网上是非常危险的事情,因此我们需要对数据加密。目前使用较多的是base64加解密。
how:

  1. 将所有数据,按照一定顺序拼接成一个大的字符串,然后加一个密钥,拼接到一起。
  2. 使用base64加密,可以加密多次
  3. 网关服务接收到data数据后,根据双方约定的密钥,加密算法,加密次数等,进行解密,并且反序列化出参数

ip白名单

why:增加安全性,防止签名或加密被破解,可以限制请求IP
how:只有在白名单中的ip地址,才能成功请求API接口,否则直接返回无访问权限。
ip白名单也可以加在API网关服务上。
但也要防止公司的内部应用服务器被攻破,这种情况也可以从内部服务器上发起API接口的请求。
这时候就需要增加web防火墙了,比如:ModSecurity等。

限流

why:并发量太高,可能导致服务直接挂掉
how:对API做限流
对请求ip做限流:比如同一个ip,在一分钟内,对API接口总的请求次数,不能超过10000次。
对请求接口做限流:比如同一个ip,在一分钟内,对指定的API接口,请求次数不能超过2000次。
对请求用户做限流:比如同一个AK/SK用户,在一分钟内,对API接口总的请求次数,不能超过10000次。
我们在实际工作中,可以通过nginx,redis或者gateway实现限流的功能。

参数校验

我们需要对API接口做参数校验,比如:校验必填字段是否为空,校验字段类型,校验字段长度,校验枚举值等等。

这样做可以拦截一些无效的请求。

比如在新增数据时,字段长度超过了数据字段的最大长度,数据库会直接报错。

但这种异常的请求,我们完全可以在API接口的前期进行识别,没有必要走到数据库保存数据那一步,浪费系统资源。

有些金额字段,本来是正数,但如果用户传入了负数,万一接口没做校验,可能会导致一些没必要的损失。

还有些状态字段,如果不做校验,用户如果传入了系统中不存在的枚举值,就会导致保存的数据异常。

由此可见,做参数校验是非常有必要的。

在Java中校验数据使用最多的是hiberate的Validator框架,它里面包含了@Null、@NotEmpty、@Size、@Max、@Min等注解。

用它们校验数据非常方便。

当然有些日期字段和枚举字段,可能需要通过自定义注解的方式实现参数校验。

统一返回值

why:返回值中有多种不同格式的返回数据,这样会导致对接方很难理解,并且不利于维护
how:可以在设计API网关时解决。

业务系统在出现异常时,抛出业务异常的RuntimeException,其中有个message字段定义异常信息。

所有的API接口都必须经过API网关,API网关捕获该业务异常,然后转换成统一的异常结构返回,这样能统一返回值结构。

统一异常处理

why:不知道你有没有遇到过这种场景:有时候在API接口中,需要访问数据库,但表不存在,或者sql语句异常,就会直接把sql信息在API接口中直接返回。

返回值中包含了异常堆栈信息、数据库信息、错误代码和行数等信息。

如果直接把这些内容暴露给第三方平台,是很危险的事情。

有些不法分子,利用接口返回值中的这些信息,有可能会进行sql注入或者直接脱库,而对我们系统造成一定的损失。
how:可以在gateway中对异常进行拦截,做统一封装,然后给第三方平台的是处理后没有敏感信息的错误信息。

请求日志

在第三方平台请求你的API接口时,接口的请求日志非常重要,通过它可以快速的分析和定位问题。

我们需要把API接口的请求url、请求参数、请求头、请求方式、响应数据和响应时间等,记录到日志文件中。

最好有traceId,可以通过它串联整个请求的日志,过滤多余的日志。

当然有些时候,请求日志不光是你们公司开发人员需要查看,第三方平台的用户也需要能查看接口的请求日志。

这时就需要把日志落地到数据库,比如:mongodb或者elastic search,然后做一个UI页面,给第三方平台的用户开通查看权限。这样他们就能在外网查看请求日志了,他们自己也能定位一部分问题。

幂等设计

why:第三方平台极有可能在极短的时间内,请求我们接口多次,比如:在1秒内请求两次。有可能是他们业务系统有bug,或者在做接口调用失败重试,因此我们的API接口需要做幂等设计。

也就是说要支持在极短的时间内,第三方平台用相同的参数请求API接口多次,第一次请求数据库会新增数据,但第二次请求以后就不会新增数据,但也会返回成功。

这样做的目的是不会产生错误数据。

how:我们在日常工作中,可以通过在数据库中增加唯一索引,或者在redis保存requestId和请求参来保证接口幂等性。

限制记录条数

why:如果请求的数据太多,很容易造成API接口超时等问题,让API接口变得不稳定。

how:通常情况下,建议一次请求中的参数,最多支持传入500条记录。

如果用户传入多余500条记录,则接口直接给出提示。

建议这个参数做成可配置的,并且要事先跟第三方平台协商好,避免上线后产生不必要的问题。

压测

why:上线前我们务必要对API接口做一下压力测试,知道各个接口的qps情况。

以便于我们能够更好的预估,需要部署多少服务器节点,对于API接口的稳定性至关重要。

之前虽说对API接口做了限流,但是实际上API接口是否能够达到限制的阀值,这是一个问号,如果不做压力测试,是有很大风险的。

比如:你API接口限流1秒只允许50次请求,但实际API接口只能处理30次请求,这样你的API接口也会处理不过来。

how: 我们在工作中可以用jmeter或者apache benc对API接口做压力测试。

异步处理

一般的API接口的逻辑都是同步处理的,请求完之后立刻返回结果。

why:但有时候,我们的API接口里面的业务逻辑非常复杂,特别是有些批量接口,如果同步处理业务,耗时会非常长。

这种情况下,为了提升API接口的性能,我们可以改成异步处理。

how:在API接口中可以发送一条mq消息,然后直接返回成功。之后,有个专门的mq消费者去异步消费该消息,做业务逻辑处理。

直接异步处理的接口,第三方平台有两种方式获取到。

第一种方式是:我们回调第三方平台的接口,告知他们API接口的处理结果,很多支付接口就是这么玩的。

第二种方式是:第三方平台通过轮询调用我们另外一个查询状态的API接口,每隔一段时间查询一次状态,传入的参数是之前的那个API接口中的id集合。

数据脱敏

有时候第三方平台调用我们API接口时,获取的数据中有一部分是敏感数据,比如:用户手机号、银行卡号等等。

why:这样信息如果通过API接口直接保留到外网,是非常不安全的,很容易造成用户隐私数据泄露的问题。

这就需要对部分数据做数据脱敏了。

我们可以在返回的数据中,部分内容用星号代替。

已用户手机号为例:182****887。

这样即使数据被泄露了,也只泄露了一部分,不法分子拿到这份数据也没啥用。

完整的接口文档

why:说实话,一份完整的API接口文档,在双方做接口对接时,可以减少很多沟通成本,让对方少走很多弯路。

how:接口文档中需要包含如下信息:

接口地址
请求方式,比如:post或get
请求参数和字段介绍
返回值和字段介绍
返回码和错误信息
加密或签名示例
完整的请求demo
额外的说明,比如:开通ip白名单。
接口文档中最好能够统一接口和字段名称的命名风格,比如都用驼峰标识命名。

接口地址中可以加一个版本号v1,比如:v1/query/getCategory,这样以后接口有很大的变动,可以非常方便升级版本。

统一字段的类型和长度,比如:id字段用Long类型,长度规定20。status字段用int类型,长度固定2等。

统一时间格式字段,比如:time用String类型,格式为:yyyy-MM-dd HH:mm:ss。

接口文档中写明AK/SK和域名,找某某单独提供等。

借鉴链接

你可能感兴趣的:(java,服务器,网络)