Restful API设计指南

这篇博文讲先后端交互的Restful API设计考虑。API必定是基于HTTP协议并尽可能靠拢Restful规范设计的。若是不了解HTTP协议,能够参考个人博文HTTP必知必会。关于Restful是怎么回事,能够考虑先参考阮一峰的博文理解RESTful架构;嫌麻烦的能够略过直接阅读个人这篇博文。html

基于资源的API设计

所谓资源,是一种对事物的抽象。一我的、一头牛、一张电影票、一句话、一个想法均可以抽象为资源。json

资源的URL表示

URL是资源的定位,是一种映射关系,即一个URL必定定位到最多不超过一个的资源。根据URL布局的特色,分为三种类型的资源:后端

  1. 集合资源api

    对于形如/users的资源称为集合资源,它表示一群用户。数组

  2. 个体资源服务器

    对于形如/users/u123的资源称为个体资源,它表示一个用户,它是经过一个惟一标识的id(即u123)来肯定这个用户。restful

  3. 子集合资源架构

    对于形如/users/u123/messages的资源称为子集合资源,它表示某个用户的一群消息,这个用户是经过惟一标识的id(即u123)来肯定的。app

补充:关于id

资源的id对于同种类型的资源来讲必定是惟一的。也就是说,肯定一个个体资源只须要经过下面的方式便可布局

/messages/m123

而没必要

/users/u123/messages/m123

资源的表述

资源自己是一种抽象,它必需要转化为一种机器可读的形式才能进行传输和处理。表述方式的例子如表单形式、XML、JSON等。能够经过HTTP头Content-Type来指定资源的表述形式,常见的例子:

  • 纯文本:text/plain
  • HTML文档:text/html
  • 表单格式:application/x-www-form-urlencoded
  • XML文档:text/xml
  • JSON文档:application/json

这里的设计中只考虑一种表述形式,即JSON。但愿这一点不会给API的调用带来困扰。

资源的操做

基本的增删改查

对于的基本的增删改查操做,除了定位到资源之外,只须要附加一个HTTP方法够了。根据Restful规范,方法对于集合的意义通常是:

  1. POST:建立
  2. GET:查询
  3. PUT:完整更新
  4. PATCH:部分更新
  5. DELETE:删除

这些方法适用于不一样的URL类型,总结以下:

  1. GET+(子)集合资源

    • GET /users 返回用户集合
    • GET /users/u123/messages 返回指定用户的消息集合
  2. POST+(子)集合资源

    • POST /users 建立一个新用户
    • POST /users/u123/messages 为指定用户建立一个新消息
  3. GET+个体资源

    • GET /users/u123 返回指定用户
  4. PUT/PATCH+个体资源

    • PUT /users/u123 更新指定用户(完整)
    • PATCH /users/u123 更新指定用户(部分)
  5. DELETE+个体资源

    • DELETE /users/u123 删除指定用户

其余的动做

当对资源的操做跳出了基本的增删改查范畴,就在资源的URL表示以后增长动做的名称便可。这能够经过两个典型的例子来阐述。

例子一:用户登陆

这个属于对集合资源进行的操做。其接口表示可设定为:

  • POST /users/login

只需在集合资源的URL表示(即/users)以后附加动做名(即/login)便可。在这种状况下,POST是典型方法。

例子二:书籍标星和去标

这是一对接口,其中一个是对一本书进行标星操做,另一个是对其进行取消标星操做。这两个接口都属于对个体资源的操做。其接口表示可分别设定为:

  • POST /books/b123/star
  • DELETE /books/b123/star

首先,须要在个体资源的URL表示(即/books/b123)以后附加动做名(即/star)。其次,这里用到对立的两个方法POST和DELETE。

例子三:查询的count

一个查询接口会返回一个资源数组,但有时咱们须要知道知足某个查询条件的资源总数。这个在集合资源的URL以后附加动做count便可。例如:

  • GET /books/count 返回书籍资源总数
  • GET /users/count?age=18 返回年龄18岁的用户资源总数

注意,这时分页控制无效。

操做的参数

在参数设计里,一个基本的设计原则是要保证简洁,避免嵌套过深。参数的格式能够根据实际状况限定为几种或不限格式。不过,至少保证表单格式和JSON格式这两种格式的支持。其中JSON格式支持任意的嵌套;表单格式对嵌套的支持不理想。

参数分为通常参数控制参数。从形式上看,控制参数如下划线开头。这么设计是为了与通常参数进行区分。通常参数例如name、age等,控制参数例如_sort等。

通用参数总结

筛选

这里的筛选只支持彻底匹配,适用于GET集合资源的接口。筛选的方式是简单直接的,直接是字段名=值的形式便可。例以下面的接口返回年龄为18的全部男性用户:

GET /users?age=18&gender=male

排序(_sort)

排序参数_sort是逗号分隔的字段列表,以+/-表示升/降序。

  • age: age升序
  • +age: age升序,同上
  • -age: age降序
  • -age,name: 先以age降序,age相等的以name升序

分页(_range)

分页参数_range的构建也是直接的,如下划线分隔它的起始和结束资源的序号。资源计数从1开始,这点要尤为注意。

  • 10_100 : 返回第10个到第100个资源集合
  • _100 : 返回第1个到第100个集合资源
  • 1_ : 返回第1个到最后一个集合资源
  • _ : 返回全部的集合资源

内嵌(_embed)

说清楚内嵌的用法,先定义一个资源Book:

{
    id: 'b123',
    title: 'xxx',
    author: {
        id: 'u123',
        name: 'yyy'
    }
}

经过GET接口获取的资源表述中,对于内嵌的资源默认只会返回其id,而不会返回其完整表述。例如调用GET /books/b123返回的数据是:

{
    id: 'b123',
    title: 'xxx',
    author: {
        id: 'u123'
    }
}

这个时候,想要获取完整的author资源,须要再调用GET /users/u123

然而,经过_embed参数,能够一步就直接返回包含完整User资源的author连接,就像咱们一开始定义的那样。调用GET /users/u123?\_embed=author,返回如下的结果:

{
    id: 'b123',
    title: 'xxx',
    author: {
        id: 'u123',
        name: 'yyy'
    }
}

多个内嵌资源用逗号分隔便可。

操做的返回值

操做的返回内容,它的表述格式是可选的。只限定为JSON格式是一种可选的简洁方案。

基于操做类型的参数和返回值模式

不一样的操做类型,其参数和返回值模式是有必定规律性的。现总结以下:

GET+(子)集合资源

这个操做请求的是一个资源集合,返回的是一个JSON数组。

其参数能包括:筛选、排序、分页、内嵌。

GET+个体资源

这个操做请求的是一个个体资源,返回的是一个JSON对象。

其参数只包括:内嵌。

POST+(子)集合资源

这个操做请求建立一个新资源,返回的是一个JSON对象,表述新建立的资源。

其参数就是该资源的表述。

PUT/PATCH+个体资源

这个操做请求更新一个旧资源,返回的是一个JSON对象,表述更新后的资源。

其参数就是该资源的表述。

DELETE+个体资源

这个操做请求删除一个资源,无返回值,无参数。

其余操做

其余操做视状况而定,通常无返回值,无参数。

操做的错误

任何操做都会发生出错状况。出错的缘由有不少种,有源于客户端的,也有源于服务器的。有预料中的,也有预料以外的。对于出错状况,服务器要向客户端发送一个状态码及附加一个JSON格式的错误消息(这里像返回值的考虑同样也只考虑JSON格式了)。一个例子就是:

401 Unauthorized
{
    message: "没登陆"
}

这里的message字段做为错误状态的描述,做为解释出错的缘由。若是表述的合适,能够做为最终用户的提示信息,直接在弹框中显示。

这里面没有考虑加入更多的字段了,好比一个code字段来更细致的区别错误的类型。这种考虑是基于客户端对于接口调用出错无能为力,通常能作的就两种:

  1. 显示错误信息;若是是幂等的接口,(可选地)重传。
  2. 若是是受权相关错误,跳转到登陆逻辑并可选地重传。

补充

验证信息

关于用户的受权在HTTP和Restful中的方案,市面上有不少种。这里介绍的是一种Token机制。很简单,用户经过某个接口(例如/users/login)获取受权的Token,而后每次调用接口时将这个Token附加到Header Access-Token中。

_token伪id

用户的Token不只用于受权,也用于标识用户的身份。当须要传递用户id的时候,每每发现用户的id信息能够从Token中获取。举个例子,id为u123的用户调用以下接口时:

GET /users/u123
Access-Token: 包含u123的受权信息

其中Token信息能够从相应的Header中获取,因此此时明确标明id u123没有必要。此时能够用一个占位符()占位一下便可。

GET /users/_token
Access-Token: 包含u123的受权信息
相关文章
相关标签/搜索