REST,全称为REpresentational State Transfer,
即表述性状态传递
是一种
应用程序的架构风格,用于构造简单、可靠、高性能的Web应用程序。
REST提出了一系列约束,遵循这些约束的应用程序称为RESTful API应用。
通常情况下,REST是基于HTTP协议而实现的。
API
应用程序编程接口,用来“连接”两个不同的系统,并使其中一方为另一方提供服务,如在操作系统上运行的应用程序能够访问操作系统所提供的API,并通过这些API来调用操作系统的各种功能。
即:
API是一个系统向外暴露或公开的一套接口,通过这些接口,外部应用程序能够访问该系统。
REST
表述性状态传递。是一种软件架构风格。作为一种Web服务的设计与开发方式,
REST可以降低开发的复杂性,提高系统的可伸缩性。
REST是一种基于资源的架构风格,在REST中,资源(Resource)是最基本的概念。任何能够命名的对象都是一个资源,如:document、user、order等,通常情况下,它表示Web服务中要操作的一个实体。
一个资源具有一个统一的资源标识符(Uniform Resource Identifier,URI),如users/
1234,通过资源标识符能够标识并访问该资源。
除了单个的资源外,资源集合表示多个相同类型的资源,如users。
在系统设计时,不同的实体之间往往存在某种关联关系,如一个用户有多个订单。同样,在REST中,这种关联关系也能够由资源之间的层次关系体现出来,如users/1234/orders/1。
由于REST以资源为中心,因此REST接口的端点(Endpoint)均以资源或资源集合结尾,它不像其他形式的Web服务一样以动词结尾,如api/GetUserInfo或api/UpdateUserInfo。在REST中,对资源的动作或操作是通过HTTP方法来完成的,如下:
[mw_shl_code=text,false]GET api.domain.com/users/1234
PUT api.domain.com/users/1234[/mw_shl_code]
当请求方发起请求,
修改了资源的状态后,更新后的资源表述应返回给请求方,这也是表述性状态传递的意义。
REST 约束
6个REST的约束(Constraint),遵循这些约束的WEB服务才是真正的RESTful服务,即:REST风格的服务。违反其中一个都不算。
(1)客户端-服务器(Client-Server)
客户端-服务器约束体现了关注点分离(Separation of Concerns)原则,使客户端与服务端各自能够独立实现并独立开发,只要它们之间的接口不改变即可;
客户端与服务端可以使用不同的技术或编程语言。
(2)统一接口(Uniform Interface)
统一接口是设计任何RESTful服务的基础,也是区别REST架构风格与其他Web服务风格的最主要约束。系统中的多个组件(包括服务端、客户端,以及可能存在的代理服务器等)都依赖于统一接口。
统一接口约束本身又由4个子约束组成,分别如下。
资源的标识
前面提到过,任何能够命名的对象都是一个资源,资源能够通过统一资源标识符来区别。对于Web系统,统一资源标识符通常是一个URL,即统一资源定位符(Uniform Resource Locator)。每个URL代表一个资源或资源集合,当访问一个URL时,能够获取该资源或对它执行相应的操作。
通过表述操作资源
当请求一个资源时,服务器返回该资源的一个表述。该表述表示资源当前的状态,它由表述正文和表述元数据组成,格式通常为JSON、XML和HTML等,比如以下代码是同一资源的两种不同的表述形式。
[mw_shl_code=json,false]{
"User": {
"id": "123",
"name": "Tom"
}
}[/mw_shl_code]
或
[mw_shl_code=xml,false]<User>
<id>1234</id>
<name>Tom</name>
</User>[/mw_shl_code]
客户端在请求资源时,能够指定期望的表述格式,服务器在返回响应时,在响应中包含了指定表述格式的资源;
访问同一个资源的不同格式无须修改资源的标识符,客户端也可以通过资源的表述(而非资源本身)对资源进行操作。
自描述消息
客户端与服务器之间传递的每一条消息都应包含足够的信息,这些信息
不仅包含了资源的表述,也包含了资源表述的相关信息(如资源表述的格式与内容长度等),甚至包含了与该资源相关的其他操作信息。
超媒体作为应用程序状态引擎(HATEOAS)
服务器返回的资源表述中不仅要包含资源的表述,也应包含与之相关的链接,这些链接能够对资源执行其他操作,比如当获取资源时,返回的链接中包含更新该资源、删除该资源等链接。
(3)分层系统(Layered System)
分层系统约束能够使网络中介(如代理或网关等)透明地部署到客户端与服务器之间,只要它们遵循并且使用前面提到的统一接口约束即可;而客户端和服务端则都不知道网络中介的存在。中间服务器主要用于增强安全、负载均衡和响应缓存等目的。
(4)缓存(Cache)
缓存是Web架构中最重构的特性之一。客户端或网络中介均能够缓存服务器返回的响应,因此当服务器返回响应时,应指明该响应的缓存特性。对响应进行缓存将有助于减少数据获取延迟以及对服务器的请求,从而提高系统的性能。
(5)无状态(Stateless)
无状态约束,指明服务器不会记录或存储客户端的状态信息,反之,这些状态信息应由客户端来保存并维护,因此客户端对服务器的请求不能依赖于已发生过的其他请求,当客户端请求服务器时,必须在请求消息中包含所有与之相关的信息(如认证信息等)。
(6)按需编码(Code-On-Demand)
按需编码约束允许服务器临时向客户端返回可执行的程序代码(如脚本等),返回这些代码主要用于为客户端提供扩展性或自定义的功能。由于客户端必须理解并能够执行服务器返回的代码,因此这一约束增加了客户端与服务器之间的耦合,同时,这一约束是可选的。
REST的错误理解
深入的理解REST,有助于我们设计RESTful服务和RESTful API。
错误的认知包含:
- 任何使用了HTTP方法的API。
- 返回JSON的API。
- 执行增删查改的API。
REST风格的API虽然有删除特点,但是并不是说具有上述特点的API就是RESTful API。
Richardson成熟度模型是衡量API成熟度的一种方式,该模型进一步描述了各种Web API的特征,根据该模型,只有最成熟的API才是RESTful API。
REST外的另一种API风格:RPC风格(即:远程过程调用,Remote Procedure Call),如下:
[mw_shl_code=text,false]GET api.domain.com/getUserInfo
GET api.domain.com/UpdateUserInfo[/mw_shl_code]
REST风格 和 RPC 风格 的区别如下:
- 在关注点方面,REST面向资源,RPC面向功能。
- 在API端点方面,REST的端点是名词、是资源或资源集合,而RPC的端点是动词、是方法名。
- 在执行特点方面,REST对资源执行操作,RPC执行服务器上的方法。
- 在返回结果方面,REST返回请求的资源,而RPC则返回调用方法的执行结果。
HTTP协议
REST是基于HTTP协议而实现的。
HTTP,超文本传输(Hyper Text Transfer Protocol,HTTP)协议,广泛的网络协议,基于 TCP/IP 协议的应用层协议。
当客户端(通常是浏览器)发起一个HTTP请求时,它首先会建立起到HTTP服务器指定端口(HTTP协议默认使用80端口)的TCP连接,而HTTP服务器则负责在该端口监听来自客户端的请求。当TCP连接成功建立后,浏览器就会向HTTP服务器发送请求命令,如GET /index.html HTTP/1.1。一旦收到请求,服务器会根据请求向客户端返回响应,其响应内容通常包括一个状态行(如HTTP/1.1200 OK)和若干个消息头,以及消息正文。消息正文则是资源、请求的文件、错误或者其他信息等。
HTTP 是明文传输的,不安全,1994年设计了HTTPS协议,即超文本传输安全协议(Hypertext Transfer Protocol Secure),也被称为HTTP over TLS,HTTP over SSL。
统一资源定位符
即通常所说的URL,代表网络上一个特定的资源。URL作为URI的子集,一个URL就是一个URI,用于标识并定位资源。
对于HTTP而言,当用户在浏览器中输入了一个URL后,意味着他想要获取或查看一些资源。
URL由以下部分组成:
- http:// ,URL协议,指明了如何访问一个特定的资源,http:// 会告诉浏览器要使用HTTP协议,即超文本传输协议;除 http:// 外,较为常见的协议还有 https:// (加密的HTTP协议)、ftp:// (文件传输协议)和 mailto: (电子邮件协议)等。
- www.….com,主机名,告诉浏览器要访问的资源所在的服务器名称。DNS(Domain Name System)服务器会将这个名称解析为一个具体的IP地址,通过这个IP地址可以找到资源所在的计算机。
- /images/logo.png,URL路径(Path),它指向服务器上具体的资源。根据要获取资源的不同,其值也会不同,可以说,这一部分的变化性最大。它有可能是在服务器的一个真实存在的文件,比如这里的/images/logo.png,也有可能是由常见的Web框架生成的动态资源,如http://www.….com/account/index。通常情况下,当访问某个网页资源时,浏览器会下载其他与此相关的资源,例如http://www.….com这个网页,它不仅包括文本信息,也包括图片、JavaScript文件、CSS以及其他资源,所有这些资源构成了我们在浏览器中看到的页面。
除了上面,还有:
- 端口号,在主机名后面,以冒号隔开。HTTP协议默认使用80端口(默认,一般忽略),如果URL中省略了端口号,则默认使用此端口;如果HTTP服务器并没有在80端口监听,而使用了其他端口,则需要在URL中指定端口号,如http://www.…. com:8080,但这种情况比较少见,通常在开发或调试Web应用时才会使用其他端口。
- 查询字符串,URL中“?”后面的参数部分,对于 http://www.….com/search?q=hello 这样一个URL,“q=hello”即为查询字符串,其中“q”是参数名,“hello”是参数值,参数名和参数值用“=”分隔。如果要传递多个参数,则使用“&”来分隔,如name1=value1&name2=value2。查询的字符串会发送给HTTP服务器,并由服务器上的Web应用程序决定如何处理这一部分的内容。
- 锚部分,也称片段(Fragment),即在“#”后面的内容,它用于指明一个资源的特定位置,例如,http://www.….com/index.html#contact,该URL将定位到HTML页面中指定的元素。这一部分内容与上述其他部分都不一样,它不会由服务器处理,只会由浏览器来处理,也就是说,若更改这一部分的内容,并不会向服务器再次发起请求,浏览器就会定位到当前资源的不同位置。
媒体类型
响应中指明资源的内容类型(Content-Type),也称媒体类型。要指定内容类型,HTTP依赖于MIME标准。
MIME(Multipurpose Internet Mail Extensions),即多用途互联网邮件扩展类型,是一种表示文档的性质和格式的标准,因此,媒体类型也被称为MIME类型。
浏览器通过MIME类型来决定如何处理文档,因此服务器在返回响应时为资源设置正确的MIME类型非常重要。
HTTP服务器会返回HTML内容,并标识其内容类型为text/html。text为主类型,后一部分html则是子类型。
而当请求一个图片资源时,根据图片文件本身的格式,HTTP服务器将返回资源的媒体类型标记为image/jpeg或image/gif。
MIME的组成结构非常简单,其语法为type/subtype,它由类型与子类型两个字符串构成,中间用“/”分隔,不允许空格存在。对大小写不敏感,但传统写法都是小写的。
常见的:text/plain: text/html: image/jpeg: image/png: application/json
HTTP消息
HTTP请求和响应消息包括以下4部分的内容:
- 起始行:即第一行,用于描述要执行的请求,或者是对应的状态,即成功或失败,这个起始行总是单行的。
- HTTP消息头:这些消息头描述了请求或响应的相关属性、配置、对消息正文的描述等。
- 空行:指明消息头已经发送完毕。
- 消息正文:包含请求数据(如要创建的资源、HTML表单内容等),或响应中资源的表述,这一部分可以为空。
请求:
[mw_shl_code=text,false]GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept-Language: zh-CN
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Host: microsoft.com
Connection: Keep-Alive[/mw_shl_code]
响应:
[mw_shl_code=text,false]HTTP/1.1 200 OK
Date: Mon, 27 Jul2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain[/mw_shl_code]
并不是所有的 请求/响应 都有正文,比如获取资源(GET)、获取资源元数据(HEAD),以及删除资源(DELETE)等请求,通常它们不需要正文,而那些要将数据从客户端发送到服务器的HTTP方法,它们若要创建资源或更新资源,就需要提供正文,比如POST和PUT等。
HTTP 方法告诉服务器当前请求要执行哪一种操作。常见的HTTP方法有GET、POST、PUT、DELETE、PATCH、HEAD和OPTIONS等。
GET:获取指定的资源,它并不会修改资源,因此GET方法是安全的。此外,GET方法也是幂等的。
- 所谓安全方法,是指不会修改资源的方法。
- 所谓幂等,是指多次对同一个URL调用同一个HTTP方法,其效果总是一样的。
POST:创建资源。不是安全方法,因为它会修改服务器上的资源;也不是幂等方法,多次请求同一个POST操作会产生多个不同的资源。
PUT:更新资源,因为PUT会修改资源,所以它不是安全的方法。与POST方法不同的是,PUT方法是幂等的,多次更新同一个资源,其返回结果都是一样的。
PUT方法除了更新资源外,当资源不存在时,它还可以创建资源。
注意,尽管POST与PUT方法都可以创建资源,但它们所请求的URI是有区别的,POST请求的URI是资源集合,而PUT则是请求单个不存在的资源,例如:
[mw_shl_code=text,false]POST api.appdomain.com/users
PUT api.appdomain.com/users/1234[/mw_shl_code]
DELETE:删除资源,它不是安全的,但它是幂等的,这意味着对同一资源请求多次DELETE方法,效果都是一样的。当第一次对资源调用DELETE方法时,返回表示操作成功的200 OK状态码,后续再调用DELETE方法,由于资源已经不存在,则应返回404 Not Found状态码。
PATCH:对资源进行部分更新,它与PUT方法的区别是:PUT会更新指定资源的全部内容,而PATCH可以根据需要仅更新资源的部分字段或属性。
HEAD:与GET方法相同,但它并不返回消息正文,在响应消息中仅包含响应状态码与消息头,该方法常用来检测资源是否存在以及获取资源的元数据。
OPTIONS:获取资源支持的操作,服务器在返回的响应中会包含Allow消息头,它的值为HTTP方法列表,例如:
[mw_shl_code=text,false]Allow: GET, POST[/mw_shl_code]
HTTP消息头
用来传递附加信息。一个消息头由消息头名称和它的值组成,中间用冒号“:”隔开,比如 Content-Type: text/plain。
常见请求头:
常见响应头:
自定义消息头,用于返回一些描述或备注类的信息。自定义消息头的名称一般以“X-”开头,以此来指明它并不是一个标准的HTTP消息头,例如X-AspNet-Version用于指明当前服务器运行的ASP.NET的版本。
状态码
由3个数字组成,用于指明HTTP请求的结果。
状态码可分为以下5类。
- 1xx:信息,服务器收到请求,需要请求方继续执行操作。
- 2xx:成功,服务器成功执行客户端所请求的操作。
- 3xx:重定向,需要进一步的操作以完成请求。
- 4xx:客户端错误,请求包含语法错误或请求内容不正确。
- 5xx:服务端错误,服务器在处理请求的过程中发生了错误。
状态码以其首位数字表示它所属的类别,而后两位则表示在该类别中具体的信息,常见状态码:
REST 的最佳实践
REST 是一种架构风格,不是标准,也就没有一套确定的、 公认的规则。
虽然有6种约束,但具体实践项目实现时,在很多细节上仍有多种多样的方式。
首先,在实现RESTful系统时,应正确地使用HTTP方法、HTTP消息头和HTTP状态码。
比如:
- 对于 HTTP 方法,应使用GET方法获取资源,使用POST方法创建资源。
- 对于 HTTP 消息头,当客户端要想指定资源的预期表述格式时,应使用Accept消息头,而非其他方式,这也正是该消息头的意义所在。
- 对于 HTTP 状态码,当操作成功却不需要返回响应正文时,应使用返回204 No Content(删除或更新资源)或201 Created(创建资源)。又如,当服务器不支持客户端指定的资源表述格式时,应返回406 Not Acceptable状态码。
除了这些原则以外,在设计资源的URI时也应注意下列原则:
使用名词的复数表示一个资源集合,如 api.domain.com/users
使用斜线“/”用来表示资源之间的层次关系,如 api.domain.com/users/1234/orders
对资源的增、删、查、改等操作名称不应包含在URL中,反之应正确使用HTTP方法。比如,GET /deleteuser/1234(错误),DELETE users/1234(正确)。
如果一个操作无法对应到资源的某个操作上,此时可以适当地在URI中包含动词,但仍然应该基于一个资源的标识符,如下所示。
[mw_shl_code=text,false]PUT /users/1234/set-admin
DELETE /users/1234/set-admin[/mw_shl_code]
查询字符串可以用来对资源进行筛选、搜索或分页查询等操作,如:
[mw_shl_code=text,false]GET /users?role=admin
GET /users?searchQuery=abc
GET /users?pageSize=25&pageNumber=2[/mw_shl_code]
URI应使用小写字母。
URI中可以使用中划线“-”来增加其可读性,使用“-”来代替空格,如:
[mw_shl_code=text,false] api.domain.com/blogs/this-is-my-first-post[/mw_shl_code]
URI中不应使用下划线。在浏览器或高级编辑器中,URI通常会带有下划线,以表示它是可点击的,这个下划线将会使URI中的下划线不可见,可以使用中划线“-”替代下划线。
URL末尾不应包含斜线“/”,尽管URL末尾包含了“/”并不影响其功能,然而末尾的“/”没有任何意义甚至还有可能会造成歧义,因此服务器返回给客户端的URL末尾不应包含“/”。
其他问题
在RESTful API中,JSON和XML是最常用到的两种资源表述格式,它们都可以用来传递数据,且都具有简洁、自描述等特点。
JSON的MIME类型为aplication/json。
JSON 的数据使用 名称/值 来表示,名称/值 包括字段名称和它的值,中间用冒号隔开。其中
字段名称应使用双引号表示,字段的
值如果是字符串,也应使用双引号表示,如"firstName" : "John"。
JSON数据项的值的类型可以是下列类型。
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true或false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
对象,在大括号{}中书写,包含多个名称/值对:
[mw_shl_code=json,false]{
"id": "1234",
"name": "Tom"
}[/mw_shl_code]
数组,多个相同的数据项构成,用方括号[]表示:
[mw_shl_code=json,false]{
"users": [
{
"id": "1234",
"name": "Tom"
},
{
"id": "1235",
"name": "Smith"
}
]
}[/mw_shl_code]
XML是可扩展标记语言,必须包含根元素,该元素是文档中其他元素的父元素。文档中的所有元素形成了一棵文档树,这棵树从根部开始,并扩展到树的最底端。
标签名区分大小写!
标签允许包含一个或多个属性,每个属性的值必须使用引号。
[mw_shl_code=xml,false]<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>[/mw_shl_code]
API 版本
当API发生了变化,如:资源表述内容有新增项(字段或属性)或系统添加了新资源类型时,应使用不同的版本来区别对API的更改
RESTful API添加版本有以下4种方式。
- 使用URI路径,如 api/v1/users。
- 使用查询字符串,如 api/users?version=v1。
- 使用自定义消息头,如 Accept-version: v1。
- 使用Accept消息头,如 Accept: application/json;v=2.0。
本章完