什么是REST以及HTTP-based RESTful API


1. REST介绍

一些概念

在理解 REST 或者 RESTful 架构之前,可以先了解一些紧密相关的概念:

  • Resources: 可以认为是网络上的任何可识别的事物,比如HTML,图像,视频,歌曲,服务等等。我们通常意义上的“上网”其实就是通过 URI 去访问、操作 Resource

    The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author’s hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.

    — Roy Fielding

  • URI (Uniform Resource Identifier): 统一资源标识符, 是明确标识特定 Resource 的字符串 。也就是说我们需要通过URI才能和特定的 Resource 互动。URLURI 的一种。

  • Resource Representation: 一项资源在任意特定时间戳上的状态(state)。Representation 通常由数据,描述数据的元数据和能够帮助客户端过渡到下一个期望状态的超媒体链接组成。 这里的数据并不一定就是资源本身,可能是资源的一种表现形式,具体是哪种形式可以通过元数据获知。

  • Status Transfer : 在与 Resource 互动的过程中,势必会牵扯到数据状态的变化,简单点来说就是数据的增删改查。

什么是 REST

REST 是 Representational State Transfer 的首字母缩写,由 Roy Thomas Fielding 在他2000年的博士论文中提出。

所以简单从字面上理解 Representational State Transfer 的话,就是“与 Representation 有关或以 Representation 为特征的状态转化”,或者说“基于 RepresentationResource 状态转化”

REST 实际上 Fielding 定义的是一种架构风格,这种风格体现在六种约束上。

  • Client-Server architecture

    Client-Server 约束背后的原理是 Separation of Concerns。Client 和 Server 之前不应该有任何依赖,Client只需要知道所要交互的 Resource 的 URI。

  • Statelessness

    Client 的每次 Request 对 Server 来说都是“全新”的,Server 不会存储任何 Client context。

  • Cacheability

    Response 必须隐式或显式地声明自己是否可缓存。

  • Layered system

    REST允许使用分层的系统架构,例如,可以在服务器A上部署API,在服务器B上存储数据,在服务器C中进行身份验证,而这些对 Client 是“隐形”的。

  • Code on demand (optional)

    Server 可以通过传输可执行代码来临时扩展或定制Client的功能。例如JavaScript之类的客户端脚本。

  • Uniform interface

    Uniform interface 是设计 RESTful 架构的基础,包含以下四个原则,

    • Resource identification in requests

      在请求中标识每个资源,比如使用URI。 并且 Resource 本身与返回给客户端的 Representation 是分开的。 例如,Server 可以从 Database 中将数据以HTML,XML或JSON的格式发送,但这些格式都不是Resource 在服务器的内部表示形式。

    • Resource manipulation through representations

      当 Client 持有 Resource 的 Representation(包括附加的任何元数据)时,它具有足够的信息来进行”State Transfer“。

    • Self-descriptive messages

      每个消息都包含足够的信息来描述如何处理该消息。例如,可以通过 media type 指定要如何解析消息。

    • Hypermedia as the engine of application state (HATEOAS)

      当访问了一个REST API的初始URI之后, Client 应该能够动态使用 Server 提供的链接来发现其所需的所有可用 Resource。 随着访问的进行,Server 将以文本作为响应,该文本包括指向当前可用其他 Resource 的超链接。

如果一个API框架满足以上六种约束,那么我们就说它是RESTful的API,或者说是RESTful架构的API。


简单的说,在REST架构风格中,数据和功能被视为 Resource ,并且可以使用 URI 进行访问。 通过使用一组简单的,定义明确的操作对 Resource进行操作。 Client 和 Server 通过使用标准化的接口和协议来交换 Resource Representation

实际上,以上是我们设计 RESTful API 的理论基础,只有了解 REST 是什么,才能有“分辨是非”的能力,才能有自己独特的理解。


2. HTTP-based RESTful API

REST vs HTTP

没有可比性,REST是一种架构风格,而HTTP是一种通讯协议。

Fielding 并未在他的论文中指示应该如何具体实现 REST,但目前通常都是基于 HTTP 协议。

如果说我们实现了一个 RESTful 风格的 API,这个 API 是基于 HTTP 协议的,我们可以说它是 HTTP-based RESTful API 。

常见的约定

假设我们的 API 最好有个专用域名,比如:

 https://api.example.com

Naming Guide

尽可能的使用一致的资源命名约定和URI格式,以最小化和最大可读性和可维护性。

假设在我们的业务逻辑中存在以下实体:

  • Employee
  • Task
  • Report

URI中应该尽量使用能够代表 Resource 的名词,而不是使用动词。

URI中应该首选小写字母

使用名词复数表示集合,比如:

GET /employees

使用集合配合 identifier 获取单个实例

GET /tasks/{taskId}

使用 / 表示层级关系,并且不要在URI尾部使用 /

GET /employees/{employeeId}/tasks
GET /employees/{employeeId}/tasks/{taskId}

使用连字符 - 分割单词可以使URI提高可读性,也更利于SEO,但不要使用下划线 _

GET /employees/{employeeId}/reports/current-month-report

不要带有文件扩展名, 依赖于通过 Content-Type 传达的媒体类型

GET /employees/{employeeId}/reports/current-month-report HTTP/1.1
content-type: application/pdf;

使用查询参数过滤集合

GET /employees/{employeeId}/tasks?top=10&skip=20
GET /employees/{employeeId}/tasks?status=completed

Http Verb

四种常用的 Http Verb

Http Verb CRUD
GET Read 返回查询的资源
POST Create 返回新建的资源
PUT Update/Replace 返回更新的资源
DELETE DELETE 返回空

比如:
获取特定 Employee 的信息

GET /employees/{emplpyeeId}

创建一个新的 Task

POST /tasks HTTP/1.1

{
	"Name":"New Task"
}

Status Code

常用的 Status Code:

Status Code Description
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] 用户请求的格式不可得
410 Gone [GET] 用户请求的资源被永久删除
422 Unprocesable entity [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误
500 INTERNAL SERVER ERROR [*] 服务器发生错误

Version Control

  1. URI 方式

    GET {version}/employees
    
  2. 自定义 Header 方式

    GET /employees HTTP/1.1
    accept-version: v2
    

Error Handle

返回对应的 StatusCode, 以 "error" 为键附带详细的错误信息。

HTTP/1.1 404 NOT FOUND

{
	"error": "Can't found employee."
}

HATEOAS

从 API 的入口可以获取针对所有资源的操作,列如

GET https://api.example.com
HTTP/1.1 200 OK

{
	"links":[{
		"rel":"collection",
		"href":"/emplpyees",
		"title":"list of employee",
		"type":"application/json"
	}]
}

rel: 当前上下文和目标是如何关联的

href: 目标资源的URI

title type : 一些属性

参考 RFC5988


以上都是“约定”而不是“守则”,RESTful API 的具体实现应该是要基于项目的实际情况,“学究”式的生搬硬套只会让自己看起来像个智障。


RMM - Richardson 成熟度模型

Leonard Richardson分析了一百种不同的Web服务设计,并根据它们与REST的兼容程度将它们分为四类。这种REST服务划分模型用于识别其成熟度级别 - 称为Richardson成熟度模型。

Richardson使用三个因素来决定服务的成熟度,即URI,HTTP方法和HATEOAS(超媒体),将成熟度分为四个等级,如下图:

  • Level 0

    使用一种通讯协议,通常是 Http, 向一个特定的 Endpoint 发送一个请求,该请求中包含了所有的细节。并且Request 和 Response 的格式可以是任何格式。比如:

    查看一个 Employee 正在做的 Task 列表

    POST /GetEmplopyeeTasks HTTP/1.1
    
    {
    	"employeeId":1,
    	"status":"processing"
    }
    

    将会得到的 Response:

    HTTP/1.1 200 OK
    
    {
    	tasks:[{
    		"id":1,
    		"status":"processing"
    	},{
    		"id":2,
    		"status":"processing"
    	}]
    }
    

    接下来可以根据这些信息去结束一个 Task:

    POST /CompleteTask HTTP/1.1
    
    {
    	"taskId":1
    }
    

    如果一切正常,可能会得到一个 Response:

    HTTP/1.1 200 OK
    
    {
    	"status":"success"
    }
    

    如果这个 Task 早就被完成了,可能会得到下面 Response

    HTTP/1.1 200 OK
    
    {
    	"status":"error",
    	"reason":"task has been completed"
    }
    

    到目前为止,这都是非常直观的基于RPC风格的系统。它是简单的,因为只有Plain Old XML(POX)在这个过程中被传输。如果你使用SOAP或者XML-RPC,原理上也是基本相同的 。

  • Level 1 - Resources

    通往 REST 风格的第一步是引入 Resource 的概念,将通过约定的 URI 命名规范来和 Resource 互动。 不是通过传入参数而调用一个函数,而是通过调用某个特定对象上的某个服务,同时将其它信息作为参数传入到该服务中 。 比如之前的 Request 可以变成:

    POST /employee/1/tasks HTTP/1.1
    
    {
    	"status":"processing"
    }
    
    POST /task/1/completion HTTP/1.1
    
  • Level 2 - HTTP Verb

    尽可能根据HTTP协议定义的那样来合理使用HTTP Verb 。

    GET /employee/1/tasks?status=processing HTTP/1.1
    
    POST /task/1/completion HTTP/1.1
    
    DELETE /task/1 HTTP/1.1
    

    并且在 Response 中使用正确的 StatusCode, 比如

    HTTP/1.1 409 Conflict
    
    {
    	"error":"task has been completed"
    }
    
  • Level 3 - Hypermedia Controls

    使用 HATEOAS


RMM的用处在于它提供了一个层层递进的思考RESTful背后本质思想的方法。正因为如此,应该将它视为一个工具来帮助我们学习概念,而不是作为某种评估机制。这一切的理论都只是为我们设计 API 框架时提供一些指导意见和思考方向,并不是必须遵守的”规定“。


3. Rest 的缺点

  • 真实系统中的资源非常复杂,很难清晰地进行资源的划分,要求开发人员要对 REST 相关的知识以及业务了解透彻。
  • 许多复杂操作无法对应到确定的 HTTP 谓词。
  • HTTP 状态码的数量有限,实际系统中的异常状态有很多。

参考文章:
理解RESTful架构 - 阮一峰
RESTful API 设计指南 - 阮一峰
What is REST

posted @ 2020-11-01 16:29  weswang  阅读(593)  评论(0)    收藏  举报