RESTful API系列 - RESTful API是什么以及其设计要点

举报
雷学委 发表于 2021/12/09 01:18:50 2021/12/09
【摘要】 ​ 目录REST API是什么?No.1 数据跟界面解耦合No.2 无状态No.3 可缓存No.4 统一接口/标准化接口No.5 分层次系统风格No.6 按需编码(可选)REST API是什么?REST中文意思是REpresentational(代表性的)State(状态)Transfer(传输),比较拗口,这个概念是Roy fielding提出的一种应用在分布式系统的架构风格。很多时候不小...

 目录


REST API是什么?

No.1 数据跟界面解耦合

No.2 无状态

No.3 可缓存

No.4 统一接口/标准化接口

No.5 分层次系统风格

No.6 按需编码(可选)


REST API是什么?

REST中文意思是REpresentational(代表性的)State(状态)Transfer(传输),比较拗口,这个概念是Roy fielding提出的一种应用在分布式系统的架构风格。

很多时候不小心写错了,我们会看到Restful API,碰到这样写法,严格说是错误的。

简单理解,RESTful API就是尽量满足以下六个设计原则来进行实现的接口,它们是:

  1. 数据跟界面解耦
  2. 无状态
  3. 可缓存
  4. 统一接口/标准化接口
  5. 分层次系统风格
  6. 按需编码(可选)

这么多个原则,换个简化的说法,即是,更清晰更标准化更易扩展的接口。

还是很抽象的感觉?

下面举几个例子一一阐述这些原则。

No.1 数据跟界面解耦合

这个很好理解,接口要朝着提供视图需要的数据的方向来设计,定制。

比如我们在做查询用户接口数据/api/user/001的响应时,应该仅仅返回一个用户的json数据

{
  "name":"levin",
  "photo":"baidu.com/ll/png234u1o343.png"
}

视图代码如下(以web页面为例子,界面代码通常是一些标签包围起来的代码块)

<div>
    <div>Name:<span>{{name}}</span></div>
    <div>Photo:<img src="{{photo}}"/></div>
</div>

而不是设计一个接口直接生成(当然这个原则并不能阻止部分程序员直接生成带数据的视图,最好不要这样做,让数据和界面分开可以有更高复用性)

<div>
   <div>Name:<span>levin</div>
   <div>Photo:<img src="baidu.com/ll/png234u1o343.png"/></div>
</div>

No.2 无状态

首先,无状态就是请求这个接口的时候,处理该请求的服务在未获取数据时,该服务是没有地方存储待获取的数据的。

举个例子,还是/api/user/001 这个接口,背后的伪代码可以如下:

let dataService = new DistributedDataService()

@Get("/api/user/{id}")
function getUser(id){
  return dataService.queryUser(id)
}

这个接口的实现调用了dataService去查询用户编号是001的信息。

服务本身是没有存储任何状态的,数据可以从数据库或者缓存中获取,这就是保证服务的无状态。

什么情况是有状态呢?

举个例子,伪代码如下:

//local data cache    
let dataService = LocalDataService()
    
@Get("/api/user/{id}")
function getUser(id){
    return dataService.queryUser(id)
}
@Delete("/api/user/{id}")
function getUser(id){
    return dataService.deleteUser(id)
}

这里使用的dataService是LocalDataService(本地的数据服务),通常单进程运行是没有任何问题的。因为无论如何操作,只要LocalDataService支持线程安全(这个需要开新的一篇来讲),它的状态严格上说不会有问题。

但在分布式系统中,普遍做法是部署多个服务器多实例,而且他们同时提供/api/user/001接口的服务。

这时候问题就来了,每个服务进程都拥有一个localDataService。

当调用来DELETE /api/user/001,只有一个服务的localDataService状态更改了(少了001用户)。

其他实例的本地数据仍有001用户,下一次查询的结果会有两种,一种结果是返回用户没找到,另一个结果是返回001用户,这就跟我们预期的不一致,导致了业务错误。

No.3 可缓存

应用缓存是一种提高性能的手段。这里官方也没有特别说明,其实符合普遍对资源的缓存的理解。

针对某个接口,可以设置请求头Cache-Control, 可以是no-cache 或者max-age= 60 (某个数值)去告诉接口请求方可以缓存该接口数据多久。

举个例子,我们进入百度搜索RFC 7234(http协议关于缓存的标准)可以看到“消息头”里面关于缓存的设置,百度把一些不经常变化的资源标记了很大值,告诉火狐浏览器,下次不需要重新下载该资源。

https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/js/tu_68114f1.js

刷新页面,我们可以拿上面缓存的url进行搜索,这里显示已经缓存了。

No.4 统一接口/标准化接口

官网提了,接口定义需要满足4个约束:资源标示、通过呈现方式来操作资源、资源自描述、以及使用超媒体作为应用状态。

举个例子,像设计 /api/user/<userId> 这个接口,如何符合第四原则满足统一标准化接口?

首先,/api/user 这个uri定位了用户资源相关的信息操作,然后可以用GET来查询用户信息,DELETE来删除用户信息。

再说资源自描述,我们查询 /api/user/1001, 获得一下响应消息:

{
   "id":"1001",
   "name":"levin",
   "country": "China"
} 

像这样的就是资源自描述,这个响应告诉了我们用户信息的属性,结构。我们查询其他人像小明,也是获取类似的反馈。

至于超媒体作为应用状态,这个不需要多说,简单理解为多种格式的响应消息作为状态即可。

这个设计原则,更多约束目标系统的REST API更加容易操作,可见可读性。

No.5 分层次系统风格

这里就是架构的分层,每层内的组件不允许看到与其不直接交互的层,也就是说,非直接交互的层对当前层内的组件不可访问。

比如上图,A <->B<->C,三层架构中,AB紧邻,BC紧邻,这样分层避免了AC直接交流,A层的变动不会对C层造成影响。

这样设计可以更好的让层与层之间互相解耦,让每一层专注做单层的功能(当然分层数过多也有坏处)。

No.6 按需编码(可选)

服务器可以提供一些代码或者脚本并在客户的运行环境中执行, 比如一些JS脚本供客户端下载调用。

像过去Java服务器端可以生成Applet脚本供客户端执行。

说到这里跟按需编码(CodeOnDemand), 好像表达的意思不太贴切,我们只需要明白这个设计原则是为了提高客户端的扩展性即可。


上面说那么多,其实RESTful API本质是通过走HTTP协议来呈现接口。

通过uri为系统交互的接口,比直接代码调用代码更加简化,而且脱离了语言约束。

更多细节参考:

What is REST

Cache-Control - HTTP | MDN

https://www.rfc-editor.org/info/rfc7234

https://www.rfc-editor.org/rfc/inline-errata/rfc2616.html

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。