互联网开发常识谱--REST

本文深入讲解REST架构的核心概念,包括其六大特征与五个关键词,并探讨REST与其他架构风格的区别。此外,还介绍了REST的设计误区及实践建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是REST?

REST全称是Representational State Transfer(表述性状态转移),REST指的是一组架构约束条件和原则。

符合以下架构约束的称为REST架构风格:

  • 客户-服务器(Client-Server)
    通信只能由客户端单方面发起,表现为请求-响应的形式。
  • 无状态(Stateless)
    通信的会话状态(Session State)应该全部由客户端负责维护。
  • 缓存(Cache)
    响应内容可以在通信链的某处被缓存,以改善网络效率。
  • 统一接口(Uniform Interface)
    通信链的组件之间通过统一的接口相互通信,以提高交互的可见性。
  • 分层系统(Layered System)
    通过限制组件的行为(即,每个组件只能“看到”与其交互的紧邻层),将架构分解为若干等级的层。
  • 按需代码(Code-On-Demand,可选)
    支持通过下载并执行一些代码(例如Java Applet、Flash或JavaScript),对客户端的功能进行扩展。

这6个特征是REST架构设计优秀程度的判断标准。其中,面向资源是REST最明显的特征,即,REST架构设计是以资源抽象为核心展开的。可寻址说的是:每一个资源在Web之上都有自己的地址。连通性说的是:应该尽量避免设计孤立的资源,除了设计资源本身,还需要设计资源之间的关联关系,并且通过超链接将资源关联起来。
这里写图片描述
REST的五个关键词:

  • 资源(Resource)
  • 资源的表述(Representation)
  • 状态转移(State Transfer)
  • 统一接口(Uniform Interface)
  • 超文本驱动(Hypertext Driven)

什么是资源?

资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。

什么是资源的表述?

资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。

什么是状态转移?

状态转移(state transfer)与状态机中的状态迁移(state transition)的含义是不同的。状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

什么是统一接口?

REST要求,必须通过统一的接口来对资源执行各种操作。对于每个资源只能执行一组有限的操作。以HTTP/1.1协议为例,HTTP/1.1协议定义了一个操作资源的统一接口,主要包括以下内容:

  • 7个HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
  • HTTP头信息(可自定义)
  • HTTP响应状态代码(可自定义)
  • 一套标准的内容协商机制
  • 一套标准的缓存机制
  • 一套标准的客户端身份认证机制

REST还要求,对于资源执行的操作,其操作语义必须由HTTP消息体之前的部分完全表达,不能将操作语义封装在HTTP消息体内部。这样做是为了提高交互的可见性,以便于通信链的中间组件实现缓存、安全审计等等功能。

什么是超文本驱动?

“超文本驱动”又名“将超媒体作为应用状态的引擎”(Hypermedia As The Engine Of Application State,来自Fielding博士论文中的一句话,缩写为HATEOAS)。将Web应用看作是一个由很多状态(应用状态)组成的有限状态机。资源之间通过超链接相互关联,超链接既代表资源之间的关系,也代表可执行的状态迁移。在超媒体之中不仅仅包含数据,还包含了状态迁移的语义。以超媒体作为引擎,驱动Web应用的状态迁移。通过超媒体暴露出服务器所提供的资源,服务器提供了哪些资源是在运行时通过解析超媒体发现的,而不是事先定义的。从面向服务的角度看,超媒体定义了服务器所提供服务的协议。客户端应该依赖的是超媒体的状态迁移语义,而不应该对于是否存在某个URI或URI的某种特殊构造方式作出假设。一切都有可能变化,只有超媒体的状态迁移语义能够长期保持稳定。

从架构风格的抽象高度来看,常见的分布式应用架构风格有三种:

  • 分布式对象(Distributed Objects,简称DO)
    架构实例有CORBA/RMI/EJB/DCOM/.NET Remoting等等
  • 远程过程调用(Remote Procedure Call,简称RPC)
    架构实例有SOAP/XML-RPC/Hessian/Flash AMF/DWR等等
  • 表述性状态转移(Representational State Transfer,简称REST)
    架构实例有HTTP/WebDAV

REST与RPC的差别在于REST支持抽象的工具是资源,RPC支持抽象的工具是过程。REST风格的架构建模是以名词为核心的,RPC风格的架构建模是以动词为核心的。简单类比一下,REST是面向对象编程,RPC则是面向过程编程。

REST典型设计误区

最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:

POST /accounts/1/transfer/500/to/2

正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:

POST /transaction HTTP/1.1
  Host: 127.0.0.1
  from=1&to=2&amount=500.00

另一个设计误区,就是在URI中加入版本号:

http://www.example.com/app/1.0/foo
  http://www.example.com/app/1.1/foo
  http://www.example.com/app/2.0/foo

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分:
  Accept: vnd.example-com.foo+json; version=1.0
  Accept: vnd.example-com.foo+json; version=1.1
  Accept: vnd.example-com.foo+json; version=2.0

REST实践

URI设计技巧

  • 使用_或-来让URI可读性更好
  • 使用/来表示资源的层级关系
  • 使用?用来过滤资源
  • ,或;可以用来表示同级资源的关系

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

HTTP 动词增删改查数据集合 (e.g. /customers)指定数据对象 (e.g. /customers/{id})
POSTCreate201 (Created), ‘Location’ header with link to /customers/{id} containing new ID.404 (Not Found), 409 (Conflict) if resource already exists…
GETRead200 (OK), list of customers. Use pagination, sorting and filtering to navigate big lists.200 (OK), single customer. 404 (Not Found), if ID not found or invalid.
PUTUpdate/Replace405 (Method Not Allowed), unless you want to update/replace every resource in the entire collection.200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid.
PATCHUpdate/Modify405 (Method Not Allowed), unless you want to modify the collection itself.200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid.
DELETEDelete405 (Method Not Allowed), unless you want to delete the whole collection—not often desirable.200 (OK). 404 (Not Found), if ID not found or invalid.

实践中常见的问题

  • POST和PUT用于创建资源时有什么区别?
    POST和PUT在创建资源的区别在于,所创建的资源的名称(URI)是否由客户端决定。 例如为我的博文增加一个java的分类,生成的路径就是分类名/categories/java,那么就可以采用PUT方法。 不过很多人直接把POST、GET、PUT、DELETE直接对应上CRUD,例如在一个典型的rails实现的RESTFul应用中就是这么做的。

  • 如果GET请求增加计数器,这是否违反安全性?
    安全性不代表请求不产生副作用,例如像很多API开发平台,都对请求流量做限制。像github,就会限制没有认证的请求每小时只能请求60次。 但客户端不是为了追求副作用而发出这些GET或HEAD请求的,产生副作用是服务端“自作主张”的。 另外,服务端在设计时,也不应该让副作用太大,因为客户端认为这些请求是不会产生副作用的。

  • 直接忽视缓存可取吗?
    即使你按各个动词的原本意图来使用它们,你仍可以轻易禁止缓存机制。 最简单的做法就是在你的HTTP响应里增加这样一个报头: Cache-control: no-cache。 但是,同时你也对失去了高效的缓存与再验证的支持(使用Etag等机制)。 对于客户端来说,在为一个REST式服务实现程序客户端时,也应该充分利用现有的缓存机制,以免每次都重新获取表示。

  • 响应代码的处理有必要吗?
    HTTP的响应代码可用于应付不同场合,正确使用这些状态代码意味着客户端与服务器可以在一个具备较丰富语义的层次上进行沟通。 例如,201(“Created”)响应代码表明已经创建了一个新的资源,其URI在Location响应报头里。 假如你不利用HTTP状态代码丰富的应用语义,那么你将错失提高重用性、增强互操作性和提升松耦合性的机会。 如果这些所谓的RESTFul应用必须通过响应实体才能给出错误信息,那么SOAP就是这样的了,它就能够满足了。

REST成熟度模型

Leonard Richardson为REST定义了一个成熟度模型,具体包含以下四个层次:

  • Level 0
    Level 0层级服务的客户端只是向服务端点发起HTTP POST请求,进行服务调用。每个请求都指明了需要执行的操作、这个操作针对的目标(例如业务对象)和必要的参数。
  • Level 1
    Level 1层级的服务引入了资源的概念。要执行对资源的操作,客户端需要发出知道要执行的操作和包含任何参数的POST请求。
  • Level 2
    Level 2层级的服务使用HTTP动词来执行操作,请求查询参数和主体(如果有的话)指定操作的参数。这让服务能够借助Web基础设施服务,例如通过CDN来缓存GET请求。
  • Level 3
    Level 3层级的服务基于HATEOAS(Hypertext As The Engine Of Application State)原则设计,基本思想是在有GET请求返回的资源信息中包含链接,这些链接能够执行该资源允许的操作。例如,客户端通过订单资源信息中包含的链接取消某一订单,或者发送GET请求去获取该订单。HATEOAS的优点包括无需在客户端代码中写入硬链接的URL。此外,由于资源信息中包含可允许操作的链接,客户端无需猜测在资源的当前状态下执行任何操作。参考

本文汇编自一些博客和文章:
理解RESTFul架构
http://mccxj.github.io/blog/20130530_introduce-to-rest.html
理解本真的REST架构风格
http://www.infoq.com/cn/articles/understanding-restful-style
理解RESTful架构
http://www.ruanyifeng.com/blog/2011/09/restful.html
Using HTTP Methods for RESTful Services
https://www.restapitutorial.com/lessons/httpmethods.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值