我们提供安全,免费的手游软件下载!

安卓手机游戏下载_安卓手机软件下载_安卓手机应用免费下载-先锋下载

当前位置: 主页 > 软件教程 > 软件教程

接口设计的18条军规

来源:网络 更新时间:2024-05-24 11:32:01

前言

之前写过一篇文章《 表设计的18条军规 》,发表之前,在全网广受好评。

今天延续设计的话题,给大家总结了接口设计的18条军规,希望对你会有所帮助。

1. 签名

为了防止API接口中的数据被篡改,很多时候我们需要对API接口做 签名

接口请求方将 请求参数 + 时间戳 + 密钥 拼接成一个字符串,然后通过 md5 等hash算法,生成一个前面sign。

然后在请求参数或者请求头中,增加sign参数,传递给API接口。

API接口的网关服务,获取到该sign值,然后用相同的请求参数 + 时间戳 + 密钥拼接成一个字符串,用相同的m5算法生成另外一个sign,对比两个sign值是否相等。

如果两个sign相等,则认为是有效请求,API接口的网关服务会将给请求转发给相应的业务系统。

如果两个sign不相等,则API接口的网关服务会直接返回签名错误。

问题来了:签名中为什么要加时间戳?

答:为了安全性考虑,防止同一次请求被反复利用,增加了密钥没破解的可能性,我们必须要对每次请求都设置一个合理的过期时间,比如:15分钟。

这样一次请求,在15分钟之内是有效的,超过15分钟,API接口的网关服务会返回超过有效期的异常提示。

目前生成签名中的密钥有两种形式:

一种是双方约定一个固定值privateKey。

另一种是API接口提供方给出AK/SK两个值,双方约定用SK作为签名中的密钥。AK接口调用方作为header中的accessKey传递给API接口提供方,这样API接口提供方可以根据AK获取到SK,而生成新的sgin。

2. 加密

有些时候,我们的API接口直接传递的非常重要的数据,比如:用户的登录密码、银行卡号、转账金额、用户身份证等,如果将这些参数,直接明文,暴露到公网上是非常危险的事情。

由此,我们需要对数据进行 加密

比如:用户注册接口,用户输入了用户名和密码之后,需要将密码加密。

我们可以使用 AES 对称加密算法。

在前端使用 公钥 对用户密码加密。

然后注册接口中,可以使用 密钥 解密,做一些业务需求校验。然后再换成其他的加密方式加密,保存到数据库当中。

3. ip白名单

为了进一步加强API接口的安全性,防止接口的签名或者加密被破解了,攻击者可以在自己的服务器上请求该接口。

需求限制请求 ip ,增加 ip白名单

只有在白名单中的ip地址,才能成功请求API接口,否则直接返回无访问权限。

ip白名单也可以加在API网关服务上。

但也要防止公司的内部应用服务器被攻破,这种情况也可以从内部服务器上发起API接口的请求。

这时候就需要增加web防火墙了,比如:ModSecurity等。

4. 限流

如果你的API接口被第三方平台调用了,这就意味着着,调用频率是没法控制的。

第三方平台调用你的API接口时,如果并发量一下子太高,可能会导致你的API服务不可用,接口直接挂掉。

由此,必须要对API接口做 限流

限流方法有三种:

  1. 对请求ip做限流:比如同一个ip,在一分钟内,对 API接口总的请求次数 ,不能超过10000次。
  2. 对请求接口做限流:比如同一个ip,在一分钟内,对 指定的API接口 ,请求次数不能超过2000次。
  3. 对请求用户做限流:比如同一个 AK/SK用户 ,在一分钟内,对API接口总的请求次数,不能超过10000次。

我们在实际工作中,可以通过 nginx redis 或者 gateway 实现限流的功能。

5. 参数校验

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

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

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

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

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

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

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

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

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

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

6. 统一返回值

我之前调用过别人的API接口,正常返回数据是一种json格式,比如:

{
    "code":0,
    "message":null,
    "data":[{"id":123,"name":"abc"}]
},

签名错误返回的json格式:

{
    "code":1001,
    "message":"签名错误",
    "data":null
}

没有数据权限返回的json格式:

{
    "rt":10,
    "errorMgt":"没有权限",
    "result":null
}

这种是比较坑的做法,返回值中有多种不同格式的返回数据,这样会导致对接方很难理解。

出现这种情况,可能是API网关定义了一直返回值结构,业务系统定义了另外一种返回值结构。如果是网关异常,则返回网关定义的返回值结构,如果是业务系统异常,则返回业务系统的返回值结构。

但这样会导致API接口出现不同的异常时,返回不同的返回值结构,非常不利于接口的维护。

其实这个问题我们可以在设计 API网关 时解决。

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

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

7. 统一封装异常

我们的API接口需要对 异常 进行统一处理。

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

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

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

有些不法分子,利用接口返回值中的这些信息,有可能会进行sql注入或者直接脱库,而对我们系统造成一定的损失。

因此非常有必要对API接口中的异常做统一处理,把异常转换成这样:

{
    "code":500,
    "message":"服务器内部错误",
    "data":null
}

返回码 code 500 ,返回信息 message 服务器内部异常

这样第三方平台就知道是API接口出现了内部问题,但不知道具体原因,他们可以找我们排查问题。

我们可以在内部的日志文件中,把堆栈信息、数据库信息、错误代码行数等信息,打印出来。

我们可以在 gateway 中对异常进行拦截,做统一封装,然后给第三方平台的是处理后没有敏感信息的错误信息。

8. 请求日志

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

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

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

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

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

9. 幂等设计

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

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

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

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

对接口幂等性感兴趣的小伙伴,可以看看我的另一篇文章《 高并发下如何保证接口的幂等性? 》,里面有非常详细的介绍。

10. 限制记录条数

对于对我提供的批量接口,一定要 限制请求的记录条数

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

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

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

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

对于一次性查询的数据太多的情况,我们需要将接口设计成分页查询返回的。

11. 压测

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

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

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

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

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

12. 异步处理

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

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

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

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

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

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

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

13. 数据脱敏

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

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

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

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

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

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

14. 完整的接口文档

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

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

  1. 接口地址
  2. 请求方式,比如:post或get
  3. 请求参数和字段介绍
  4. 返回值和字段介绍
  5. 返回码和错误信息
  6. 加密或签名示例
  7. 完整的请求demo
  8. 额外的说明,比如:开通ip白名单。

接口文档中最好能够统一接口和字段名称的命名风格,比如都用 驼峰标识 命名。

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

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

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

最近建了一些高质量的粉丝群,里面可以交流技术,有工作内推,有粉丝福利。

进群方式

添加苏三的私人微信:su_san_java,备注:博客园+粉丝,即可加入。

15. 请求方式

接口支持的请求方式有很多,比如:GET、POST、PUT、DELETE等等。

我们在设计接口的时候,要根据实际情况选择使用哪种请求方式。

实际工作中使用最多的是: GET POST ,这两种请求方式。

如果没有输入参数的接口,可以使用GET请求方式,问题不大。

如果有输入参数的接口,推荐使用POST请求方式,坑更少。

主要原因有下面两点:

  1. POST请求方式更容易扩展参数,特别是在Fegin调用接口的场景下,比如:增加一个参数,调用方可以不用修改代码。而GET请求方式,需要修改代码,否则编译会出错。
  2. GET请求方式的参数,有长度限制,最长是5000个字符,而POST请求方式对参数的长度没有做限制,可以传入更长的参数值。

16. 请求头

对于一些公共的功能,比如:接口的权限验证,或者接口的traceId参数。

我们在设计接口的时候,不用把所有的参数,都放入接口的请求参数中。

有些参数可以放到 Header 请求头中。

比如:我们需求记录每个请求的traceId,不用在所有接口中都加traceId字段。

而改成让用户在header中传入traceId,在服务端使用统一的拦截器解析header,即可获取该traceId了。

17. 批量

我们在设计接口的时候,无论是查询数据、添加数据、修改数据,还是删除的场景,都应该考虑一下能否设计成 批量 的。

很多时候,需要通过id查询数据详情,比如:通过订单id,查询订单详情。

如果你的接口只支持,通过一个id,查询一个订单的详情。

那么,后面需要通过多个id,查询多个订单详情的时候,就需要额外增加接口了。

如果你添加数据的接口,只支持一条数据一条数据的添加。

后面,有个job需要一次性添加1000条数据的时候,这时在代码中循环1000次,一个个添加,这种做法效率比较低。

为了让你的接口设计的更加通用,满足更多的业务场景,能设计成批量的,尽量别设计成单个的。

18. 职责单一

我之前见过有些小伙伴设计的接口,在入参中各种条件都支持,在Service层有N多的if...else判断。

而且返回的实体类中,包含了各种场景下的返回值字段,字段很多很全。

接口上线一年之后,自己可能都忘了,在哪些业务场景下,要传入哪些字段,返回值是哪些字段。

这类接口的维护成本非常高,而且又不敢轻易重构,怕改了A业务场景,影响B业务场景的功能,这种接口让人非常痛苦的。

好的接口设计原则是: 职责单一

比如用户下单的场景,有web端和移动端。

而每个端都有普通下单和快速下单,两种不同的业务场景。

我们在设计接口的时候,可以将web端和移动端的接口在controller层完全分开。

/web/v1/order
/mobile/v1/order

并且将普通下单和快速下单也分开:

/web/v1/order/create
/web/v1/order/fastCreate
/mobile/v1/order/create
/mobile/v1/order/fastCreate

这样可以设计成4个接口。

业务逻辑更清晰一些,方便后面维护。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注苏三的公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。