https://juejin.im/entry/6844903503953920007

[译] RESTful API 设计最佳实践

阅读 8779

收藏 0

2017-10-16

原文链接: segmentfault.com

原文:RESTful API Design. Best Practices in a Nutshell.
作者:Philipp Hauer


项目资源的URL应该如何设计?用名词复数还是用名词单数?一个资源需要多少个URL?用哪种HTTP方法来创建一个新的资源?可选参数应该放在哪里?那些不涉及资源操作的URL呢?实现分页和版本控制的最好方法是什么?因为有太多的疑问,设计RESTful API变得很棘手。在这篇文章中,我们来看一下RESTful API设计,并给出一个最佳实践方案。

每个资源使用两个URL

资源集合用一个URL,具体某个资源用一个URL:

/employees         #资源集合的URL
/employees/56      #具体某个资源的URL

用名词代替动词表示资源

这让你的API更简洁,URL数目更少。不要这么设计:

/getAllEmployees
/getAllExternalEmployees
/createEmployee
/updateEmployee

更好的设计:

GET /employees
GET /employees?state=external
POST /employees
PUT /employees/56

用HTTP方法操作资源

使用URL指定你要用的资源。使用HTTP方法来指定怎么处理这个资源。使用四种HTTP方法POST,GET,PUT,DELETE可以提供CRUD功能(创建,获取,更新,删除)。

  • 获取:使用GET方法获取资源。GET请求从不改变资源的状态。无副作用。GET方法是幂等的。GET方法具有只读的含义。因此,你可以完美的使用缓存。
  • 创建:使用POST创建新的资源。
  • 更新:使用PUT更新现有资源。
  • 删除:使用DELETE删除现有资源。

2个URL乘以4个HTTP方法就是一组很好的功能。看看这个表格:

  POST(创建) GET(读取) PUT(更新) DELETE(删除)
/employees 创建一个新员工 列出所有员工 批量更新员工信息 删除所有员工
/employees/56 (错误) 获取56号员工的信息 更新56号员工的信息 删除56号员工

对资源集合的URL使用POST方法,创建新资源

创建一个新资源的时,客户端与服务器是怎么交互的呢?

在资源集合URL上使用POST来创建新的资源过程:

  1. 客户端向资源集合URL/employees发送POST请求。HTTP body 包含新资源的属性 “Albert Stark”。
  2. RESTful Web服务器为新员工生成ID,在其内部模型中创建员工,并向客户端发送响应。这个响应的HTTP头部包含一个Location字段,指示创建资源可访问的URL。

对具体资源的URL使用PUT方法,来更新资源

使用PUT更新已有资源:

  1. 客户端向具体资源的URL发送PUT请求/employee/21。请求的HTTP body中包含要更新的属性值(21号员工的新名称“Bruce Wayne”)。
  2. REST服务器更新ID为21的员工名称,并使用HTTP状态码200表示更改成功。

推荐用复数名词

推荐:

/employees
/employees/21

不推荐:

/employee
/employee/21

事实上,这是个人爱好问题,但复数形式更为常见。此外,在资源集合URL上用GET方法,它更直观,特别是GET /employees?state=externalPOST /employeesPUT /employees/56。但最重要的是:避免复数和单数名词混合使用,这显得非常混乱且容易出错。

对可选的、复杂的参数,使用查询字符串(?)。

不推荐做法:

GET /employees
GET /externalEmployees
GET /internalEmployees
GET /internalAndSeniorEmployees

为了让你的URL更小、更简洁。为资源设置一个基本URL,将可选的、复杂的参数用查询字符串表示。

GET /employees?state=internal&maturity=senior

使用HTTP状态码

RESTful Web服务应使用合适的HTTP状态码来响应客户端请求

  • 2xx - 成功 - 一切都很好
  • 4xx - 客户端错误 - 如果客户端发生错误(例如客户端发送无效请求或未被授权)
  • 5xx – 服务器错误 - 如果服务器发生错误(例如,尝试处理请求时出错)
    参考维基百科上的HTTP状态代码。但是,其中的大部分HTTP状态码都不会被用到,只会用其中的一小部分。通常会用到一下几个:
2xx:成功 3xx:重定向 4xx:客户端错误 5xx:服务器错误
200 成功 301 永久重定向 400 错误请求 500 内部服务器错误
201 创建 304 资源未修改 401未授权  
    403 禁止  
    404 未找到  

返回有用的错误提示

除了合适的状态码之外,还应该在HTTP响应正文中提供有用的错误提示和详细的描述。这是一个例子。请求:

GET /employees?state=super

响应:

// 400 Bad Request
{"message": "You submitted an invalid state. Valid state values are 'internal' or 'external'","errorCode": 352,"additionalInformation" : "http://www.domain.com/rest/errorcode/352"
}

使用小驼峰命名法

使用小驼峰命名法作为属性标识符。

{ "yearOfBirth": 1982 }

不要使用下划线(year_of_birth)或大驼峰命名法(YearOfBirth)。通常,RESTful Web服务将被JavaScript编写的客户端使用。客户端会将JSON响应转换为JavaScript对象(通过调用var person = JSON.parse(response)),然后调用其属性。因此,最好遵循JavaScript代码通用规范。
对比:

person.year_of_birth // 不推荐,违反JavaScript代码通用规范
person.YearOfBirth // 不推荐,JavaScript构造方法命名
person.yearOfBirth // 推荐

在URL中强制加入版本号

从始至终,都使用版本号发布您的RESTful API。将版本号放在URL中以是必需的。如果您有不兼容和破坏性的更改,版本号将让你能更容易的发布API。发布新API时,只需在增加版本号中的数字。这样的话,客户端可以自如的迁移到新API,不会因调用完全不同的新API而陷入困境。 使用直观的 “v” 前缀来表示后面的数字是版本号。

/v1/employees

你不需要使用次级版本号(“v1.2”),因为你不应该频繁的去发布API版本。

提供分页信息

一次性返回数据库所有资源不是一个好主意。因此,需要提供分页机制。通常使用数据库中众所周知的参数offset和limit。

/employees?offset=30&limit=15 #返回30 到 45的员工

如果客户端没有传这些参数,则应使用默认值。通常默认值是offset = 0limit = 10。如果数据库检索很慢,应当减小limit值。

/employees #返回0 到 10的员工

此外,如果您使用分页,客户端需要知道资源总数。例:请求:

GET /employees

响应:

{"offset": 0,"limit": 10,"total": 3465,"employees": [//...]
}

非资源请求用动词

有时API调用并不涉及资源(如计算,翻译或转换)。例:

GET /translate?from=de_DE&to=en_US&text=Hallo
GET /calculate?para2=23&para2=432

在这种情况下,API响应不会返回任何资源。而是执行一个操作并将结果返回给客户端。因此,您应该在URL中使用动词而不是名词,来清楚的区分资源请求和非资源请求。

考虑特定资源搜索和跨资源搜索

提供对特定资源的搜索很容易。只需使用相应的资源集合URL,并将搜索字符串附加到查询参数中即可。

GET /employees?query=Paul

如果要对所有资源提供全局搜索,则需要用其他方法。前文提到,对于非资源请求URL,使用动词而不是名词。因此,您的搜索网址可能如下所示:

GET /search?query=Paul //返回 employees, customers, suppliers 等等.

在响应参数中添加浏览其它API的链接

理想情况下,不会让客户端自己构造使用REST API的URL。让我们思考一个例子。
客户端想要访问员工的薪酬表。为此,他必须知道他可以通过在员工URL(例如/employees/21/salaryStatements)中附加字符串“salaryStatements”来访问薪酬表。这个字符串连接很容易出错,且难以维护。如果你更改了访问薪水表的REST API的方式(例如变成了/employees/21/salary-statement/employees/21/paySlips),所有客户端都将中断。
更好的方案是在响应参数中添加一个links字段,让客户端可以自动变更。
请求:

GET /employees/

响应:

//...{"id":1,"name":"Paul","links": [{"rel": "salary","href": "/employees/1/salaryStatements"}]},
//...

如果客户端完全依靠links中的字段获得薪资表,你更改了API,客户端将始终获得一个有效的URL(只要你更改了link字段,请求的URL会自动更改),不会中断。另一个好处是,你的API变得可以自我描述,需要写的文档更少。
在分页时,您还可以添加获取下一页或上一页的链接示例。只需提供适当的偏移和限制的链接示例。

GET /employees?offset=20&limit=10
{"offset": 20,"limit": 10,"total": 3465,"employees": [//...],"links": [{"rel": "nextPage","href": "/employees?offset=30&limit=10"},{"rel": "previousPage","href": "/employees?offset=10&limit=10"}]
}

相关阅读:

  • 作者写的一篇关于在Java中测试RESTful服务的最佳实践的文章。
  • 作者强烈推荐一本书Brain Mulloy’s nice paper,作为这篇文章的基础。
  • API
  • 设计
  • 服务器
  • 后端

hellostory

实际开发中,对外暴露简单接口倒还可以,如果系统所有功能都按RESTFul开发,简直找死,或许是本人能力不够吧。比如登录、注销、上传文件、审批、反审批、更改单据状态、修改订单数量、保存订单排序、投料、暂停、恢复…… 各种想到没想到的功能,你怎么分类

2年前

1

回复

雪中鱼01

后端开发

是啊,我也在想,不知道如果有些复杂逻辑处理的接口要怎么制定api,但是好像好多大公司在用restful api,像twitter、instagramer,不知道他们怎么处理的~

1年前

回复

leto40438

web 前端 @ 字节跳动

回复

雪中鱼01

: 这就体现出经验的作用了吧,不只是熟悉规则就行,还需要大量的实际开发经验1年前

回复

雪中鱼01

后端开发

回复

leto40438

: 这是句看似有道理的废话,技术的迭代不就是要减少靠经验的操作吗?弓箭要想射的准就得靠经过良好训练的弓箭手,手枪的发明不就是让人人都可以进行设计吗?如果一个发明的代价是增加了另一个需要大量经验的过程,那这个发明就是错的,无用的1年前

1

回复

ikeguang

数据开发

回复

雪中鱼01

: 同意1年前

回复

叶嘉祺同学

可以参考 google api convention:cloud.google.com/apis/design。规范是为了提高效率,同时方便大家之间对接

2月前

回复

七者

JAVA后端

赞!刚好需要了解restful

2年前

回复

下载掘金客户端

一个帮助开发者成长的社区

相关文章

一文快速入门分库分表(送给不知该学点啥的你)

38

5

谈谈前后端分离及认证选择

22

0

手写一个抖音视频去水印工具,千万别刚一个程序员

123

79

还在手写任务调度代码?试试这款可视化分布式调度框架!

15

2

探讨技术团队文化和 996 之间的关系

11

21

分享

| 掘金浏览器插件 - 打

[译] RESTful API 设计最佳实践相关推荐

  1. RESTful API 设计最佳实践

    2019独角兽企业重金招聘Python工程师标准>>> 背景 目前互联网上充斥着大量的关于RESTful API(为方便,下文中"RESTful API "简写为 ...

  2. 来自Google资深工程师的API设计最佳实践

    来自Google资深工程师Joshua Bloch的分享:API设计最佳实践 为什么API设计如此重要?API是一个公司最重要的资产. 为什么API的设计对程序员如此重要? API一旦发布,出于兼容性 ...

  3. 设计实用的RESTful API的最佳实践

    关于REST API设计的文章.<星际战士>游戏中暴露API的部分 API是开发人员的用户界面--所以要努力让它变得令人愉快 使用RESTful url和操作 在任何地方都使用SSL,没有 ...

  4. 阿里研究员谷朴:API 设计最佳实践的思考

    2019独角兽企业重金招聘Python工程师标准>>> API是软件系统的核心,而软件系统的复杂度Complexity是大规模软件系统能否成功最重要的因素.但复杂度Complexit ...

  5. 深度 | API 设计最佳实践的思考

    API 是模块或者子系统之间交互的接口定义.好的系统架构离不开好的 API 设计,而一个设计不够完善的 API 则注定会导致系统的后续发展和维护非常困难. 接下来,阿里巴巴研究员谷朴将给出建议,什么样 ...

  6. 一文详解 API 设计最佳实践

    -     前言    - 良好设计的API = 快乐的程序员

  7. 关于RestFul API 介绍与实践

    之前演示的PPT,直接看图... •参考链接: •RESTful API 设计最佳 实践 •RESTful API 设计 指南 • SOAP webserivce 和 RESTful webservi ...

  8. 简述使用REST API 的最佳实践

    Facebook,Google,Github,Netflix,Amazon和Twitter等许多巨头都拥有自己的REST(ful)API,您可以访问它们来获取甚至写入数据. 但是,为什么所有都需要RE ...

  9. mongodb数据合并设计_「时间序列数据」和MongoDB(二)-模式设计最佳实践

    在上一篇博客文章时间序列数据与MongoDB:第一部分-简介中,我们介绍了时间序列数据的概念,然后介绍了一些可以用于帮助收集时间序列应用程序需求的发现问题.对这些问题的回答有助于指导支持大容量生产应用 ...

最新文章

  1. python编程输入标准-揭秘python编程技巧
  2. MDCC 2016:网易云信直击移动IM之痛
  3. PC端连接Android设备进行adb调试
  4. 华为oj题目c语言,华为OJ机试题目——24点游戏算法
  5. 如何学习编程?顺便介绍些好的网站
  6. php oauth 扩展,PHP扩展之Web服务(一)——OAuth
  7. python备份文件最简单案例_Python实现备份文件实例
  8. 面试官就是这么欺负人:new Object()到底占用几个字节?
  9. 用promise封装ajax_回调、使用Promise封装ajax()、Promise入门
  10. then是java关键字吗_then是java关键字吗
  11. 【工具推荐】免费的思维导图软件——Blumind
  12. 【前端面试题】数据类型-js
  13. 远程监控有效保护家庭安全
  14. vue结合Waterfall做图片瀑布流展示
  15. 【sv】enum赋值
  16. uniapp遮罩_APP新手引导遮罩层设计与UI视觉界面设计欣赏
  17. 安卓的图片占用内存,图片分辨率,图片适配不同屏幕的研究
  18. 中国最好的职业TOP10
  19. 解决模拟器不能上网问题
  20. 关于使用条码打印机指令打印汉字的问题

热门文章

  1. HDU - 6955 Xor sum tire树 + 贪心
  2. Pagodas HDU - 5512
  3. [数据结构专训][GXOI/GZOI2019]旧词,[hdu5118]GRE Words Once More!,[hdu6333]Problem B. Harvest of Apples
  4. 内存管理(ybtoj-二叉堆)
  5. UOJ#84-[UR #7]水题走四方【dp】
  6. AT2165-[AGC006D]MedianPyramidHard【二分,贪心】
  7. nssl1511-我的世界【堆,贪心】
  8. P3195-[HNOI2008]玩具装箱【斜率优化dp】
  9. ssl提高组周六模拟赛【2019.3.2】
  10. codeforces1494 D. Dogeforces(构造)