思考:实际项目中如何做 RESTful接口 设计规范

>>2020,微服务装逼指南

点击上方“Java知音”,选择“置顶公众号”

技术文章第一时间送达!

作者:浮生若梦

juejin.im/post/5afee0e86fb9a07ab379b371

前言

这篇文章,主要想总结自己在设计RESTful API的一系列经验思考。

有些规范可能与标准规范有所出入,但是所有的考量都是基于『减少重复工作,增加可读性可维护性』而出发的。

对 RESTful API 的认识

说到这个,似乎又要谈及『RESTful 是对资源的抽象』、『结合了HTTP 的特点』云云。不过这些都是一些没用的套话,没啥营养价值,而我想从另外的角度谈论这个。

既然是 API,一般都符合 API 的一般模式:

ResultType ApiName(ParamType )

  1. 接口参数,即形参。可以是 string,int,以及其他任意可以称之为参数的东西

  2. 接口返回值。可以是 string,int,以及其他任意可以称之为返回值的东西

  3. 接口名(签名)

我们来看看 RESTful 是如何对应上这个模式的:

HttpResponse URL(HttpRequest)

  1. HttpRequest:包括请求头,URL参数,请求body参数

  2. HttpResponse: 包括响应头,响应的body

这样来看,RESTful API 无非是一种特殊的API 而已,通用的 API 设计法则,同样适合 RESTful,只不过非变换形式而已。

那么我们大概有哪些比较通用的标准呢?大概有这些:

  • 接口命名,必须做到清晰。一般来说,做到『动宾短语』即可。

  • 接口数量,越少越好。三个不如两个,两个不如一个,一个不如没有,最好的 API 就是『没有API』。

  • 有明确的输入输出。念念不忘必有回响,总是有返回值,告诉调用端,我到底做了什么,做得怎么样,即:反馈。

下面就来看看这些标准,是如何影响下文的内容的,:)。

URL设计,及其反模式

URL 就是接口签名,而签名必须做到清晰,没有歧义。

有统一的前缀 & 版本化

如果后端架构是服务化的,那么有可能每个服务会对外提供公共的 RESTful API,那么有个统一的前缀格式,会比较好,比如:

/SERVICE_NAME/v1/users
or
/APP_NAME/v2/users

尽量短小

同一份资源,可以有不同的路径,去理解它。比如:

User -- 1:N --> Server-- 1:N --> Client … 更加复杂的实体映射关系

1. /users/{user_id}/servers/{server_id}/clients
2. /clients

一般大家倾向于选项 1(但是实体关联关系特复杂时,会缩短URL),不过选项2 也是一个不错的选择,总而言之,口味问题吧。

数量尽量少

接口数量越少越好,能合并的接口就尽量合并。比如,这样的情况:

  • 获取用户列表信息:GET /users

  • 获取单个用户信息:GET /users/{id}

坦白说,获取一个与获取一批,似乎并没有什么语义上的差别,但是后端的同学就不一样了,他可能需要写两个 View Class。

所以只保留批量的接口,查询一个时,用 URL 参数传递就行了。

这样的情况:

PUT /users/{id}
PUT /users

直接合并到一个接口里面做就行了,PUT 一个 user与 PUT 一群 user,有啥本质的不同吗?

还有这样极端的情况:

DELETE /users/{id}

删除一个user与删除一批user,有啥不同?

如果要一次删除100 个 user,难道让前端同学,调 100 次这个接口?

多一次调用,就多一次风险(如网络问题),这个时候就别守着 RESTful 那些个教条了,接口的可用性、效率性,更加重要。

这个时候,不如设计成这样(至于 DELETE 接口能不能传Request body,这里不讨论):

POST /users_deletion
{
    "user_ids": [1, 2, 3, 4, 5]
}

返回值设计

前面有说到,HttpResponse中,我们可以利用:

  • Response Headers
    可以做少量文章,如自定义一个Header

  • Status Code
    按照基本规范来,该404的404,该200的200

  • Response body
    基本都是围绕这个做文章

Response body 既要能正常返回信息,出错了也要告诉出错原因(错误码),出错详情。所以我们大概可以设计成这样:

{
    是否成功
    boolean "is_success":
    错误码是多少
    number|null "err_code":
    错误信息
    string|null "err_msg"
    错误详情(可选)
    string|null "err_detail":
    出错的时哪个服务
    string|null "provider"

    正常返回时的数据
    "response_data": {

    }
}

这样,前端调用 API ,就有章法可循了,不至于盲目。

字段命名规范

没有很明确的规范,但是尽量跟随数据库的风格,即:下划线风格。

这样,在 序列化整个 Model 时,也许会很方便。

其他规范

接口限流

参考 GitHub 的风格。

接口安全

这个没法系统化,可以参考网络上相关文章。

END

Java面试题专栏

【40期】说一下线程池内部工作原理
【39期】Mybatis面试18问,你想知道的都在这里了!
【38期】一份tcp、http面试指南,常考点都给你了
【37期】请你详细说说类加载流程,类加载机制及自定义类加载器
【36期】说说 如何停止一个正在运行的线程?
【35期】谈谈你对Java线程之间通信方式的理解
【34期】谈谈为什么要拆分数据库?有哪些方法?
【33期】分别谈谈联合索引生效和失效的条件
【32期】你知道Redis的字符串是怎么实现的吗?
【31期】了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?应对措施是什么


思考:实际项目中如何做 RESTful接口 设计规范

我知道你 “在看思考:实际项目中如何做 RESTful接口 设计规范

原文始发于微信公众号(Java知音):思考:实际项目中如何做 RESTful接口 设计规范