RESTful设计方法和规范
在初步了解了 RESTful 之后,我们接到一项任务,需要为一所学校开发一套师生管理系统,客户要求所开发的系统能在 PC 桌面通过浏览器使用,而且日后还想开发 IOS 和 Android 应用。了解需求之后,我们毫不犹豫选择了前后端分离的开发模式,并且决定遵从时下最为流行的 RESTful 规范。接下来,我们就以后端开发人员的角色,一起来了解整个开发过程。
1. 域名(Domain)
根据 RESTful 规范,应该尽量使用专用的域名用于部署 API,于是我们和校方沟通,使用下方域名作为 API 访问地址:
但是经过沟通,发现上述域名已被占用,校方否决了我们的提议,考虑到 API 相对简单,于是我们使用下面地址部署 API:
上述地址中,https 代表协议名称,常见的还有 http,二者区别在于前者在传输过程中是将信息加密后传输的,而后者是明文传输;api 代表一个资源路径,可以想象成这台电脑中一个文件夹的路径。
2. 版本(Versioning)
师生管理系统不是一成不变的,日后还要更新维护。为了区分不同版本,API 的 URL 中应当包含 API 版本信息:
除了上述方法外,API 版本信息还可放在 HTTP 请求头中。Github 采用的就是这种做法。
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个 URL。版本号可以在 HTTP 请求头信息的 Accept 字段中进行区分(参见Versioning REST Services):
实际工作中,通常采用第一种方法,因为这样的方式更加直观,方便使用。
3. 路径(Endpoint)
路径即”终点”(endpoint),是访问 API 的具体网址,通过访问每个网址,可以获取到相应的资源(resource)。在师生管理系统中,所谓资源,就是我们想获取的信息,比如获取 3 年 2 班所有学生姓名,获取小明的年龄、成绩等。
路径须满足以下规范:
1. 资源路径中应当使用名词,杜绝动词。资源路径中的名词,应当与数据库的表名相对应。
以下路径中包含动词,是不符合规范的例子,在实际工作中,应当避免。
对于资源的操作,应该通过 HTTP 中的不同方法来区分处理资源的动作,资源路径中应当只包含名词。
2. API 中的名词应该使用复数。无论是子资源或者是所有资源。
例如:
4. HTTP动词
对于资源的具体操作类型,由 HTTP 动词表示。
常用的 HTTP 动词有下面 4 个(括号里是对应的 SQL 命令)。
GET(SELECT):从服务器取出资源(一项或多项)
POST(CREATE):在服务器新建一个资源
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
DELETE(DELETE):从服务器删除资源
还有 3 个不常用的 HTTP 动词。
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)
HEAD:获取资源的元数
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的
下面是一些例子。
5. 过滤信息(Filtering)
如果记录数量很多,服务器不可能都将它们返回给用户。API 应该提供参数,过滤返回结果。比如,我们想获取全校师生的个人信息,如果将这些信息一股脑地全部展示在网页上,是不明智也是不现实的。如果数据量太大,在实际开发中我们会采用分页展示的形式。另外,如果想在一次考试后,按照成绩高低展示学生信息,那么可以通过过滤信息来实现。
所谓过滤,就是在 URL 中添加一下限制参数。下面是一些常见的参数。
参数的设计允许存在冗余,即允许 API 路径和 URL 参数允许有重复。比如,想要查询某个班级所有学生信息,我们可以设计 GET /classes/ID/students 与 GET /students?class_id=ID 两种地址,任何一种都可得到相同的结果。
为什么上面的两种路径的请求结果一样呢,可以在这里说明一下。
6. 状态码(Status Codes)
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的 HTTP 动词)。不同的状态码代表着不同的含义,比如以 2 开头的状态码通常代表服务器成功响应,3 开头的状态码代表发生了重定性(即跳转到了别的链接),4 开头的状态码通常表示客户端这边提供的信息有误,而 5 开头的状态码则表示服务器内部出现的错误。通过返回的状态码,用户即可判断请求成功与否,不成功问题在何处。
一些常用的状态码列举如下:
200 OK – [GET]:服务器成功返回用户请求的数据
201 CREATED – [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted – [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT – [DELETE]:用户删除数据成功
400 INVALID REQUEST – [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized – [*]:表示用户没有权限(令牌、用户名、密码错误)
403 Forbidden – [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的
404 NOT FOUND – [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的
406 Not Acceptable – [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的
422 Unprocesable entity – [POST/PUT/PATCH]: 当创建一个对象时,发生一个验证错误
500 INTERNAL SERVER ERROR – [*]:服务器发生错误,用户将无法判断发出的请求是否成功
状态码的完全列表参见这里或这里。
7. 错误信息
如果状态码是 4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息是键值对形式的数据,将 error 作为键名,出错信息作为键值即可。比如,在一个提供查询学生信息的 API 中,要求客户端提供正确的 API key(可以理解为输入了正确的用户名和密码)才能访问,如果提供的 API key 不正确,此时服务器应拒绝访问,并返回错误信息。这样,客户端就知道了为何没能查到信息,修改成正确的 API key 即可。
8. 返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
9. 超媒体链接
RESTful API 最好做到 Hypermedia(即返回结果中提供链接,连向其他 API 方法),使得用户不查文档,也知道下一步应该做什么。
比如,Github 的 API 就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
从上面可以看到,如果想获取当前用户的信息,应该去访问 api.github.com/user,然后就得到了下面结果。
上面代码表示,服务器给出了提示信息,以及文档的网址。
10. 数据格式
服务器返回的数据格式,应该尽量使用 JSON,避免使用 XML。什么是 JSON 呢?什么又是 XML 呢?两种数据格式的简单举例如下:
通过上面的对比可以看出,JSON 数据形式要远比 XML 的数据形式来得简单和易懂,所以现在的 Web 开发中 JSON 数据格式已经开始全面取代 XML 应用在实际开发中。
11. 小结
本节主要从域名、版本、路径、HTTP动词、过滤信息、状态码、错误信息、返回结果、超媒体链接、数据格式 10 个方面介绍了 RESTful 设计方法和设计规范。为了让更多的人方便使用所设计的 API 接口,以上规范一定要遵守哦!