图文详解 RESTful

tech2025-05-23  7

一、什么是RESTful ?

       RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。

       而REST全称是Representational State Transfer,中文意思是表述性状态转移。 REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

 

二、 RESTful特点

RESTFUL特点包括:

每一个URI代表1种资源;客户端使用GET、POST、PUT、DELETE,这4个操作方式来对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;通过操作资源的表现形式来操作资源;资源的表现形式是XML或者JSON;客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

三、RESTful与 RPC的区别

        使用 RPC 样式的架构构建的基于 SOAP 的 Web 服务成为实现 SOA 最常用的方法。RPC 样式的 Web 服务客户端将一个装满数据的信封(包括方法和参数信息)通过 HTTP 发送到服务器。服务器打开信封并使用传入参数执行指定的方法。方法的结果打包到一个信封并作为响应发回客户端。客户端收到响应并打开信封。每个对象都有自己独特的方法以及仅公开一个 URI 的 RPC 样式 Web 服务,URI 表示单个端点。它忽略 HTTP 的大部分特性且仅支持 POST 方法

         由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法。可以使用各种语言(比如 Java 程序、Perl、Ruby、Python、PHP 和 Javascript[包括 Ajax])实现客户端。RESTful Web 服务通常可以通过自动客户端或代表用户的应用程序访问。所以,这种服务的简便性可以让用户能够与之直接交互,使用它们的 Web 浏览器构建一个 GET URL 并读取返回的内容。

        在 REST 样式的 Web 服务中,每个资源都有一个地址。资源本身都是方法调用的目标,方法列表对所有资源都是一样的。这些方法都是标准方法,包括 HTTP GET、POST、PUT、DELETE,还可能包括 HEAD OPTIONS

         在 RPC 样式的架构中,关注点在于方法,而在 REST 样式的架构中,关注点在于资源 —— 将使用标准方法检索并操作信息片段。

          REST-RPC 混合 Web 服务不使用信封包装方法、参数和数据,而是直接通过 HTTP 传输数据,这与 REST 样式的 Web 服务是类似的。但是它不使用标准的 HTTP 方法操作资源。它在 HTTP 请求的 URI 部分存储方法信息。

 

四、RESTful架构

        RESTful架构是对MVC架构改进后所形成的一种架构,通过使用事先定义好的接口与不同的服务联系起来。在RESTful架构中,通过浏览器使用POST、DELETE、PUT和GET四种请求方式分别对指定的URL资源进行增删改查操作。因此,RESTful是通过URI实现对资源的管理及访问,具有扩展性强、结构清晰的特点。

        RESTful架构将服务器分成前端服务器和后端服务器两部分,前端服务器为用户提供无模型的视图;后端服务器为前端服务器提供接口。浏览器向前端服务器请求视图,通过视图中包含的AJAX函数发起接口请求获取模型。

        项目开发引入RESTful架构,利于团队并行开发。在RESTful架构中,将多数HTTP请求转移到前端服务器上,降低后端服务器的负荷,使视图获取后端模型失败也能呈现。但RESTful架构却不适用于所有的项目,当项目比较小时无需使用RESTful架构,会使得项目变得更加复杂。

 

五、理解RESTful

  5. 1 资源与URI

        要让一个资源可以被识别,需要有个唯一标识,在Web中这个唯一标识就是URI(Uniform Resource Identifier)。

        URI既可以看成是资源的地址,也可以看成是资源的名称。如果某些信息没有使用URI来表示,那它就不能算是一个资源, 只能算是资源的一些信息而已。URI的设计应该遵循可寻址性原则,具有自描述性,需要在形式上给人以直觉上的关联。

这里以github网站为例,给出一些还算不错的URI:

https://github.com/githttps://github.com/git/githttps://github.com/git/git/blob/master/block-sha1/sha1.hhttps://github.com/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08https://github.com/git/git/pullshttps://github.com/git/git/pulls?state=closedhttps://github.com/git/git/compare/master…next

下面让我们来看看URI设计上的一些技巧:

使用 “_” 或 “-” 来让URI可读性更好

曾经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。

 5.2 统一资源接口

       RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET、PUT、POST、DELETE,并遵循这些方法的语义。

如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性,例如GET和HEAD请求都是安全的, 无论请求多少次,都不会改变服务器状态。而GET、HEAD、PUT和DELETE请求都是幂等的,无论对资源操作多少次, 结果总是一样的,后面的请求并不会产生比第一次更多的影响。

下面列出了GET,DELETE,PUT和POST的典型用法:

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)- 服务端当前无法处理请求

 

5.3 资源的表述

        上面提到,客户端通过HTTP方法可以获取资源,是吧? 不,确切的说,客户端获取的只是资源的表述而已。 资源在外界的具体呈现,可以有多种表述(或成为表现、表示)形式,在客户端和服务端之间传送的也是资源的表述,而不是资源本身。 例如文本资源可以采用html、xml、json等格式,图片可以使用PNG或JPG展现出来。

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

那么客户端如何知道服务端提供哪种表述形式呢?

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

以github为例,请求某组织资源的json格式的表述形式:

假如github也能够支持xml格式的表述格式,那么结果就是这样的:

下面我们来看一些实践上常见的设计:

在URI里边带上版本号

有些API在URI里边带上版本号,例如:

http://api.example.com/1.0/foohttp://api.example.com/1.2/foohttp://api.example.com/2.0/foo

如果我们把版本号理解成资源的不同表述形式的话,就应该只是用一个URL,并通过Accept头部来区分,还是以github为例,它的Accept的完整格式是:application/vnd.github[.version].param[+json]

对于v3版本的话,就是Accept: application/vnd.github.v3。对于上面的例子,同理可以使用使用下面的头部:

Accept: vnd.example-com.foo+json; version=1.0Accept: vnd.example-com.foo+json; version=1.2Accept: vnd.example-com.foo+json; version=2.0

使用URI后缀来区分表述格式

像rails框架,就支持使用/users.xml或/users.json来区分不同的格式。 这样的方式对于客户端来说,无疑是更为直观,但混淆了资源的名称和资源的表述形式。 我个人认为,还是应该优先使用内容协商来区分表述格式。

如何处理不支持的表述格式

当服务器不支持所请求的表述格式,那么应该怎么办?若服务器不支持,它应该返回一个HTTP 406响应,表示拒绝处理该请求。下面以github为例,展示了一个请求XML表述资源的结果:

5.4 状态的转移

      我们先来讨论一下REST原则中的无状态通信原则。初看一下,好像自相矛盾了,既然无状态,何来状态转移一说?

其实,这里说的无状态通信原则,并不是说客户端应用不能有状态,而是指服务端不应该保存客户端状态。

5.4.1 应用状态与资源状态

      实际上,状态应该区分应用状态和资源状态,客户端负责维护应用状态,而服务端维护资源状态。

     客户端与服务端的交互必须是无状态的,并在每一次请求中包含处理该请求所需的一切信息。

     服务端不需要在请求间保留应用状态,只有在接受到实际请求的时候,服务端才会关注应用状态。

     这种无状态通信原则,使得服务端和中介能够理解独立的请求和响应。

    在多次请求中,同一客户端也不再需要依赖于同一服务器,方便实现高可扩展和高可用性的服务端。

    但有时候我们会做出违反无状态通信原则的设计,例如利用Cookie跟踪某个服务端会话状态,常见的像J2EE里边的JSESSIONID。这意味着,浏览器随各次请求发出去的Cookie是被用于构建会话状态的。

    当然,如果Cookie保存的是一些服务器不依赖于会话状态即可验证的信息(比如认证令牌),这样的Cookie也是符合REST原则的。

5.4.2 应用状态的转移

状态转移到这里已经很好理解了, "会话"状态不是作为资源状态保存在服务端的,而是被客户端作为应用状态进行跟踪的。客户端应用状态在服务端提供的超媒体的指引下发生变迁。服务端通过超媒体告诉客户端当前状态有哪些后续状态可以进入。

这些类似"下一页"之类的链接起的就是这种推进状态的作用——指引你如何从当前状态进入下一个可能的状态。

最新回复(0)