REST和Ajax

文章目录

      • 什么是REST
      • RESTful设计指南
        • 使用名词来表示资源
        • 关注请求头
        • 合理使用请求方法和状态码
        • 正确地使用REST
        • 对输出的结果不再包装
        • 不要做错误的提示
        • 使用嵌套对象序列化
        • 版本
        • URI失效和迁移
        • 信息过滤
        • 速度限制
        • 缓存
        • 并发控制
      • 使用Ajax

什么是REST

REST(Representational State Transfer),常见的翻译是“表现层状态转化”。

REST其实省略了主语:资源,表现层实际上指的是资源的表现层。资源是指Web上一切可以识别、可命名、可找到并被处理的实体。比如HTML页面、音频文件、图片等。用一个URI(统一资源定位符)指向资源,使用HTTP请求方法操作资源,URI可以进一步划分为统一资源名(URN,代表资源的名字)和统一资源定位符(URL,代表资源的地址),其中URL可以定位HTTP网址、FTP服务器和文件路径等,符合绝大多数场景,所以一般都可以用URL代替URI。

REST架构风格最重要的架构约束有如下5个:

1. 客户端-服务器端。这种Client/Server的架构形式提供了基本的分布式,客户端发起请求,服务端决定响应或者拒绝请求,如果出错则返回错误信息,由客户端处理异常。
2. 无状态。通信的会话状态应该全部由客户端负责维护,也就是请求种包含了全部必要的信息。如果使用基于服务器端端会话,要么需要保证指定会话会使用同一个服务器响应所有请求,要么得创建一个可供所有服务器访问的公用的会话存储区,对每个请求都额外访问这个集中式的数据储存区获得会话状态。
3. 缓存。无状态就表示可能出现重复的请求,事实上这些请求只需要第一次真正的执行,其余的请求都可以享用这个已完成的结果而直接响应,所以缓存可以抵消一部分无状态带来的影响。
4. 统一接口。统一接口意味着每个REST应用都共享一种通用架构,那些熟悉这种架构的人一眼就能看明白接口的意义,并会继续延承下去。
5. 分层系统图。将系统划分为几个部分,每个部分负责一部分相对单一的职责,然后通过上层对下层的依赖和调用组成一个完整的系统。通常可以划分为如下三层。
  - 应用层:负责返回JSON数据和其他业务逻辑
  - 服务层:为应用层提供服务支持,如全站的账号系统
  - 数据访问层:提供数据访问和存储的服务,如数据库,缓存系统,文件系统,搜索引擎等。

REST就是这一系列设计约束的集合,如果一个架构符合REST原则,就称它为RESTful架构。

RESTful设计指南

API一旦发布其结构将很难修改,因此设计和实现一个符合规范,灵活,友好的API,是一件非常重要的事情。

使用名词来表示资源

URI不应该包含动词。动词应该通过不同的HTTP方法来体现,如下是几种常见的错误用法:

  • GET /getusers/1
  • POST /users/1/delete
  • POST /users/1/create

正确的用法是:

  • GET /users/1
  • DELETE /users/1
  • PUT /users/1

关注请求头

一定要看请求头信息,并给予正确的状态码。举个例子,假设服务器只能返回JSON格式,如果客户端的头信息的Accept字段要求返回application/xml,这个时候就不应该返回application/json类型的数据,而应该返回406错误。

合理使用请求方法和状态码

不能一味使用GET和POST,返回200,不合理的请求方法可能让未来的维护者或者合作方感到迷惑。关于方法语义的说明,可参考下表:

1.1 方法语义的说明
方法 语义
OPTIONS 用于获取资源支持的所有HTTP方法
HEAD 用户只获取请求某个资源返回的头信息
GET 用于从服务器获取某个资源的信息
1.完成请求后,返回状态码200 OK
2.完成请求后,需要返回被请求的资源详细信息
POST 用于创建新资源:
1.创建完成后,返回状态码201 Created
2.完成请求后,需要返回被创建的资源详细信息
PUT 用于完整的替换资源或者创建指定身份的资源,比如创建id为123的某个资源
1.如果是创建了资源,则返回201 Created
2.如果是替换了资源,则返回200 OK
PATCH 用于局部更新资源
1.完成请求后,返回状态码200 OK
2.完成请求后,需要返回被修改的资源的详细信息
DELETE 用于删除某个资源,完成请求后返回状态码204 No Content

正确地使用REST

REST服务器是无状态的。在有分页的时候,它并不能知道你当前访问到了什么位置,前一页和后一页的地址是什么,这个关系需要客户端来维护。但是“下一页资源”这样的业务逻辑是需要服务端来提供。例如,下面的例子返回是不完整的:

Status: 200 OK
[
    {
        "id": 1,
        "url": "https://api.linyl.com/users/1/",
    },
    {
        "id": 2,
        "url": "https://api.linyl.com/users/2/",
    }
]

返回的结果没有告诉我们是否有下一页,也没有告诉我们符合条件的记录总数。可以添加Link和X-Total-Count头来提供这样的功能。

Status: 200 OK
X-Total-Count: 210
Link: ; rel="next",
	  ; rel="last"
[
    {
        "id": 1,
        "url": "https://api.linyl.com/users/1/",
    },
    {
        "id": 2,
        "url": "https://api.linyl.com/users/2/",
    }
]

rel的值还可以是first、self和prev,客户端只需要根据Link中提供的链接就可以找到全部的符合条件的条目。

还有两处容易出错的地方需要留意:

  • 使用“201 Created“响应时,应该带Location, 指向新建资源的地址。
  • 使用“405 Method Not Allowed“响应时,应该带有Allow头,告诉客户端对该资源有效的HTTP方法。

对输出的结果不再包装

body中应该直接放数据,不要多层封装。下面有个不恰当的响应的例子:

HTTP/1.1 200 OK
{
    'success': trut,
    'data': {'id': 1, 'name': xiaoming},
}

直接返回data中的数据就好了:

HTTP/1.1200 OK
{'id': 1, 'name': 'xiaoming}

因为通过状态码“200 OK”就可以知道结果是正确的,就没有必要添加“success”字段。

如果API使用者确实由于某种原因无法访问返回头,或者API需要支持交叉域请求(例如通过jsonp),这两种情况下还是需要包装的。

不要做错误的提示

当访问出错或者响应的结果不符合预期时,不应该返回200作为状态码。哪怕返回的结果中也包含了错误原因,因为在没有充分的文档说明前提下,客户端可能会缓存成功的HTTP请求。

使用嵌套对象序列化

对象应该合理地嵌套,不应该都在一个层次上。如下的格式是不正确的:

{
    'id': 1,
    'post_id': '1001',
    'post_name': 'Post1',
    'post_content': 'this is a post'
}

尽可能把相关联的资源信息内联在一起。应该把post作为一个键:

{
    'id': 1,
    'post': {
        'id': '1001',
        'name': 'Post1',
        'content': 'this is a post'
    }
}

版本

常见的区分版本的方法有三种:

  • 保存在URI中。比如https://api.linyl.com/api/v2
  • 放在请求头中。比如GitHub的用法:Accept: application/vnd.github.v3+json
  • 自定义请求头。比如,X-Api-Version: 1

第三种方式不推荐,推荐使用第一种。

URI失效和迁移

随着业务发展,会出现一些API失效或者迁移。对失效的API,应该返回“404 not found“ 或”410 gone“;对迁移的API,返回301重定向。

信息过滤

URI通常最好越简短越好,对结果过滤、排序和搜索相关的功能都应该通过参数实现。一些常见的参数用法如表1.2所示。

表1.2 常见的参数及其用法
参数 含义
offset=0&limit=10 指定返回记录的数量,offset也可以用start这个名字
offset=10 指定返回记录的开始位置
page=2 &per_page=100 指定第几页,以及每页的
sortby=namespace &order=asc 指定返回结果按照哪个属性排序,以及排序的顺序
sort=age,desc 多个排序条件组合

速度限制

为了避免请求泛滥,给API设置速度限制很重要。为此,RFC6585引入了HTTP状态码429(too many requests)。加入速度限制功能之后,应该提示用户。可以参照GitHup的返回头,如下所述。

  • X-RateLimit-Limit:当前时间段允许的并发请求数。
  • X-RateLimit-Remaining:当前时间段保留的请求数。
  • X-RateLimit-Reset:当前时间段剩余的秒数。
HTTP/1.1 200 OK
Date: Mon, 26 Nov 2018 14:24:03 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 1350
Server: GitHub.com
Status: 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1543245843
Cache-Control: public, max-age=60, s-maxage=60
Vary: Accept

缓存

数据内容在一段时间不会变动,这个时候我们就可以合理地减少HTTP响应内容。应该在响应头中携带Last-Modified、ETag、Vary、Date等信息,客户端可以在随后请求这些资源时,在请求头中使用If-Modified-Since、If-None-Match等来确认资源是否经过修改。如果资源没有做过修改,那么就可以响应“304 Not Modified“,并且不在响应实体中返回任何内容。

并发控制

缺少并发控制等PUT和PATCH请求可能导致“更新丢失”。这个时候可以使用Last-Modified和ETag头来实现条件请求。具体规则如下:

  • 客户端发起的请求如果没有包含If-Unmodified-Since或者If-Match头,就返回状态码“403 Forbidden”,在响应正文中解释为何返回该状态码。
  • 客户端发起的请求所提供的If-Unmodified-Since或者If-Match头与服务器记录的实际修改时间或ETag值不匹配时,返回状态码“412 Precondition Failed”。
  • 客户端发起的请求所提供的If-Unmodified-Since或者If-Match头与服务器记录的实际修改时间或ETag的历史值匹配,但资源已经被修改过时,返回状态码“409 Conflict“。
  • 客户端发起的请求所提供的条件符合实际值,就更新资源,响应”200 OK“或者”204 No Content“,并且包含更新过的Last-Modified和/或ETag头,同时包含Content-Location头,其值为更新后的资源URI。

使用Ajax

你可能感兴趣的:(其他)