RESTful接口设计

1 什么是RESTful

  REST全称是Representational State Transfer,中文意思是表述性状态转移。首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。他在论文中提到:”我这篇文章的写作目的,就是想在架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。“如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

  RESTful架构设计风格,提供了设计原则和约束条件。

2 为什么要是用RESTful请求风格

  目前RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。

  RESTful是一种设计风格,并非一个标准。因此,即使完全不用这种风格,功能上也能够满足需求。
  但是它结构清晰、符合标准、易于理解、扩展方便,越来越多网站在采用它

3 理解RESTful

  我们首先需要理解Representational State Transfer这个词组到底是什么意思,它的每一个词都有些什么含义。

  结合REST原则,围绕资源展开讨论,从资源的定义、获取、标书、关联、状态变迁等角度,列举一些关键概念并加以解释。

  • 资源与URI
  • 统一资源接口
  • 资源的表述
  • 资源的链接
  • 状态的转移

3.1 资源与URI

  REST全称是表述性状态转移,这里的表述其实指的就是资源。任何事物只要有被引用的必要,它就是一个资源。资源可以是实体,可以是一个抽象的概念。

  要让一个资源可以被识别,需要有个唯一标识,在Web中这个唯一的标识就是==URI(统一资源标识符)==(Uniform Resource Identifier)。URI就可以看成资源的地址,也可以看成是资源的名称。如果某些信息没有使用URI表示,那他就不能算是一个资源,只能算是资源的一些信息而已。URI的设计应该遵循可寻址性原则,具有自描述性,需要在形式上给人直觉上的关联。以github网站为例:

曾经Web上的URI都是冰冷的数字或者无意义的字符串,但现在越来越多的网站使用_或-来分隔一些单词,让URI看上去更为人性化。 例如国内比较出名的开源中国社区,它上面的新闻地址就采用这种风格, 如http://www.oschina.net/news/38119/oschina-translate-reward-plan。

  • 使用/来表示资源的层级关系

例如上述/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08就表示了一个多级的资源, 指的是git用户的git项目的某次提交记录,又例如/orders/2012/10可以用来表示2012年10月的订单记录。

  • 使用?用来过滤资源

很多人只是把?简单的当做是参数的传递,很容易造成URI过于复杂、难以理解。可以把?用于对资源的过滤, 例如/git/git/pulls用来表示git项目的所有推入请求,而/pulls?state=closed用来表示git项目中已经关闭的推入请求, 这种URL通常对应的是一些特定条件的查询结果或算法运算结果。

  • ,或;可以用来表示同级资源的关系

有时候我们需要表示同级资源的关系时,可以使用,或;来进行分割。例如哪天github可以比较某个文件在随意两次提交记录之间的差异,或许可以使用/git/git /block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca作为URI。 不过,现在github是使用…来做这个事情的,例如/git/git/compare/master…next。

3.2 统一资源接口

  RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET,PUT和POST,并遵循这些方法的语义。REST还要求,对于自愿执行的操作,操作语义必须由HTTP消息体之前的部分完全表述,不能将操作语义冯哲在HTTP消息体内部。

以前

http://127.0.0.1/item/queryItem.action?id=1 (查询,GET)
http://127.0.0.1/item/saveItem.action (新增,POST)
http://127.0.0.1/item/updateItem.action (更新,POST)
http://127.0.0.1/item/deleteItem.action?id=1 (删除,GET或POST)

现在 http://127.0.0.1/item

以下是HTTP方法的典型用法:

GET

  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)

  • 200(OK) - 表示已在响应中发出

  • 204(无内容) - 资源有空表示

  • 301(Moved Permanently) - 资源的URI已被更新
  • 303(See Other) - 其他(如,负载均衡)
  • 304(not modified)- 资源未更改(缓存)
  • 400 (bad request)- 指代坏请求(如,参数错误)
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求

POST

  • 不安全且不幂等
  • 使用服务端管理的(自动产生)的实例号创建资源
  • 创建子资源
  • 部分更新资源
  • 如果没有被修改,则不过更新资源(乐观锁)

  • 200(OK)- 如果现有资源已被更改

  • 201(created)- 如果新资源被创建

  • 202(accepted)- 已接受处理请求但尚未完成(异步处理)
  • 301(Moved Permanently)- 资源的URI被更新
  • 303(See Other)- 其他(如,负载均衡)
  • 400(bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求

PUT

  • 不安全但幂等
  • 用客户端管理的实例号创建一个资源
  • 通过替换的方式更新资源
  • 如果未被修改,则更新资源(乐观锁)

  • 200 (OK)- 如果已存在资源被更改

  • 201 (created)- 如果新资源被创建

  • 301(Moved Permanently)- 资源的URI已更改
  • 303 (See Other)- 其他(如,负载均衡)
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 409 (conflict)- 通用冲突
  • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
  • 415 (unsupported media type)- 接受到的表示不受支持
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务当前无法处理请求

DELETE

  • 不安全但幂等
  • 删除资源

  • 200 (OK)- 资源已被删除

  • 301 (Moved Permanently)- 资源的URI已更改

  • 303 (See Other)- 其他,如负载均衡
  • 400 (bad request)- 指代坏请求
  • 404 (not found)- 资源不存在
  • 409 (conflict)- 通用冲突
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求

3.3 资源的表述

  客户端通过HTTP方法可以获取资源,确切地说客户端获取的知识资源的表述而已。资源在外界的具体呈现,可以有多种表述形式,在客户端和服务端之间传送的也是资源的表述,而不是资源本身。例如文本资源可以采用html、xml、json等格式,图片可以使用png或jpg来呈现。

  ==URI只代表资源的实体,不代表它的形式==,它的具体表现形式应该是HTTP请求头信息中用Accept和Content-Type字段指定。

  资源的表述包括数据和描述数据的元数据,例如,HTTP头“Content-Type”就是这样一个元数据属性。

  客户端是如何指导服务端提供哪些表述形式?

  答案是可以通过HTTP内容协商,客户端可以通过Accept头请求一种特定格式的表述,服务端通过Content-Type告诉客户端资源的表述形式。

4 设计RESTful接口

4.1 资源设计规则

以前的接口:

发布帖子:www.bbc.com/bbs_post?title=xx&content=xxx

修改帖子:www.bbc.com/bbs_update?title=xx&content=xxx

删除帖子:www.bbc.com/bbs_delete?title=xx&content=xxx

这种方式的问题:存在大量的接口方法,URL地址设计复杂

在RESTful架构中,每个网址代表一种资源,所以网址中不能有动词,只能是==名词==,而且所用的名字往往与数据库的表格名对应。一般来说,数据库中的表都是记录同种数据的“集合”(collection),所以URI中的名词也应该使用==复数==。

www.xxx.com/videos

4.2 动作设计规则

方法 作用
GET(select) 从服务器取出资源
POST(create) 在服务器新建一个资源
PUT(update) 在服务器中更新资源(客户端提供改变后的完整资源),PUT更新整个对象
PATCH(update) 在服务器更新资源(客户端提供改变的属性【补丁】),PATCH更新个别属性
DELETE(delete) 从服务器中删除资源

动作示例:

GET /videos:列出所有的视频

POST /videos:添加一个视频

GET /videos/id:获取某个视频的信息

PUT /videos/id:更新某个视频的信息(提供该视频的全部信息)

PATCH /videos/id:更新某个视频的信息(提供该视频的部分信息)

DELETE /videos/id:删除某个视频

4.3 返回结果

  1. 一个API可以允许返回JSON,XML甚至HTML等文档格式;建议使用JSON

  2. 以前通过URL来规定获取的格式类型,比如:

    xxx/employee.json;

    xxx/employee.html等

但是更建议使用Accpet这个请求头

Accept与Content-Type的区别

  1. Accept属于请求头,Content-Type属于实体头

    Http报头分为通用报头,请求报头,响应报头和实体报头

    请求方的http报头结构:通用报头|请求报头|实体报头
    响应方的http报头结构:通用报头|响应报头|实体报头

  2. Accept代表发送端(客户端)希望接受的数据类型

    比如:Accept: application/json;

    代表客户端希望接受的数据类型是json类型,后台返回json数据

Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型

比如:Accept: application/json;

代表发送端希望接受的数据类型是json类型,后台就要以json格式来接收前端发过来的数据

两者结合起来:

Accept: application/json;

Content-Type; application/json;

即代表希望接受的数据是json格式,本次请求发送的数据的数据格式也是json

5 Java中常见的RESTful开发框架

  • Jersey
  • play
  • SpringMVC

6 restful风格API使用规则

可以简单理解为restful风格把网址简化了,同一个网址使用不同的请求方法(GET、POST、PUT、DELETE)获得不同的数据,相比传统的接口设计更加简便。根据项目的需求,可以按照这样的规则进行设计,方便之后前后端的联调。

  • URI链接中使用名词复数

    如果存在文章方面的功能需求

    不要使用:

    www.ccnnaa.com/getAllArticle

    www.ccnnaa.com/createArticle

    建议使用:

    www.ccnnaa.com/articles

  • 子资源表达关系

    GET /article/124598 返回id为123598的文章

  • 方法使用规则

    | 方法 | 作用 |
    | ——————— | —— |
    | GET(select) | 查 |
    | POST(create) | 新建 |
    | PUT(update) | 修改 |
    | DELETE(delete) | 删除 |