Restful API 最佳实践

前后端分离和微服务成为现代软件开发的大趋势下,API设计也应该变得越来越规范和高效。

1. RESTful

REST(英文:Representational State Transfer,简称REST),RESTful是一种对基于HTTP的应用设计风格,只是提供了一组设计原则和约束条件,而不是一种标准。

从本质上理解RESTful,它其实是尽可能复用HTTP特性来规范软件设计,甚至提高传输效率。HTTP包处于网络应用层,因此HTTP包为平台无关的字符串表示,如果尽可能的使用HTTP的包特征而不是大量在body定义自己的规则,可以用更简洁、清晰、高效的方式实现同样的需求。

请求方法 资源访问路径 数据操作说明
GET /users/ 获取所有用户数据
GET /users/1 获取id=1的用户数据
POST /users 新增一条用户数据
DELETE /users/1 删除id=1的用户数据

RESTful的本质是基于HTTP协议对资源的增删改查操作做出定义。

1.1 几个典型的RESTful API场景:
功能 URL HTTP Method
获取一组数据列表 /base-path/records GET
根据ID获取某个数据 /base-path/records/{recordID} GET
新建数据 /base-path/records POST
完整地更新数据 /base-path/records/{recordID} PUT
部分更新数据 /base-path/records/{recordID} PATCH
删除 /base-path/records/{recordID} DELETE
夸域访问预请求 /base-path/records/{recordID} OPTION

虽然HTTP协议定义了其他的Method,但是就普通场景来说,用好上面的几项已经足够了。

RESTful的几个注意点:

  • URL只是表达被操作的资源位置,因此不应该使用动词,且注意单复数区分

  • 除了POST和DELETE之外,其他的操作需要冥等的,例如对数据多次更新应该返回同样的内容

  • 设计风格没有对错之分,RESTful一种设计风格,与此对应的还有RPC甚至自定义的风格

  • RESTful和语言、传输格式无关

  • 无状态,HTTP设计本来就是没有状态的,之所以看起来有状态因为我们浏览器使用了Cookies,每次请求都会把Session ID(可以看做身份标识)传递到headers中。关于RESTful风格下怎么做用户身份认证我们会在后面讲到。

  • RESTful没有定义body中内容传输的格式,有另外的规范来描述怎么设计body的数据结构,网络上有些文章对RESTful的范围理解有差异

1.2 避免多级 URL

常见的情况是,资源需要多级分类,因此很容易写出多级的 URL,比如获取某个作者的某一类文章。

1
GET /authors/12/categories/2

这种 URL 不利于扩展,语义也不明确,往往要想一会,才能明白含义。

更好的做法是,除了第一级,其他级别都用查询字符串表达。

1
GET /authors/12?categories=2

下面是另一个例子,查询已发布的文章。你可能会设计成下面的 URL。

1
GET /articles/published

查询字符串的写法明显更好。

1
GET /articles?published=true

2. JSON API

因为RESTful风格仅仅规定了URL和HTTP Method的使用,并没有定义body中数据格式的。我们怎么定义请求或者返回对象的结构,以及该如何针对不同的情况返回不同的HTTP 状态码?

API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。因此,服务器回应的 HTTP 头的Content-Type属性要设为application/json。

2.1 MIME 类型

JSON API数据格式已经被IANA机构接受了注册,因此必须使用application/vnd.api+json类型。因此,客户端请求时,必须要明确告诉服务器,可以接受 JSON 格式,即请求的 HTTP 头的ACCEPT属性也要设成application/json。

1
2
GET /orders/2 HTTP/1.1 
Accept: application/json
2.2 JSON文档结构

在顶级节点使用data、errors、meta,来描述数据、错误信息、元信息。

注意:data和errors应该互斥,不能再一个文档中同时存在,meta在项目实际上用的很少,只有特别情况才需要用到,比如返回服务器的一些信息。

2.3 data属性

一个典型的data的对象格式,我们的有效信息一般都放在attributes中。

data属性

  • id显而易见为唯一标识,可以为数字也可以为hash字符串,取决于后端实现

  • type 描述数据的类型,可以对应为数据模型的类名

  • attributes 代表资源的具体数据

  • relationships、links为可选属性,用来放置关联数据和资源地址等数据

2.4 errors属性

这里的errors和data有一点不同,一般来说返回值中errors作为列表存在,因为针对每个资源可能出现多个错误信息。最典型的例子为,我们请求的对象中某些字段不符合验证要求,这里需要返回验证信息,但是HTTP状态码会使用一个通用的401,然后把具体的验证信息在errors给出来。

errors属性

在title字段中给出错误信息,如果我们在本地或者开发环境想打出更多的调试堆栈信息,我们可以增加一个detail字段让调试更加方便。需要注意的一点是,我们应该在生产环境屏蔽部分敏感信息,detail字段最好在生产环境不可见。

3 常用的返回码

  • 不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
  • 正确设置http状态码,不要自定义;
  • Response body 提供 1) 错误的代码(日志/问题追查);2) 错误的描述文本(展示给用户)。

常用的http状态码及使用场景:

状态码 使用场景
200 ok 请求成功
201 created POST创建资源返回成功的标志
400 bad request 常用在参数校验
401 unauthorized 未经验证的用户,常见于未登录。如果经过验证后依然没权限,应该 403(即 authentication 和 authorization 的区别)。
403 forbidden 无权限
404 not found 资源不存在
500 internal server error 非业务类异常
503 service unavaliable 由容器抛出,自己的代码不要抛这个异常

参考文献:

细说API - 重新认识RESTful

RESTful API 最佳实践

理解RESTful架构

Restful API 的设计规范

微软API指南

updated updated 2024-01-01 2024-01-01
本文结束感谢阅读

本文标题:Restful API 最佳实践

本文作者:woodwhales

原始链接:https://woodwhales.cn/2019/01/27/021/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%