首页 > 其他分享 >04 \| 工整与自由的风格之争:SOAP和REST

04 \| 工整与自由的风格之争:SOAP和REST

时间:2024-02-15 15:11:40浏览次数:40  
标签:HTTP 04 URL REST 风格 POST SOAP

作者: 四火

完成时间:

总结时间:

你好,我是四火。

今天我要邀请两位风格迥异的主角登上舞台,一位西装革履,另一位随性洒脱。前面那位,代表着工整、严谨和细致;后面那位,代表着自由、灵活和简约。

它们来自两个不同的时代,却同时活跃于当今的互联网,并担当着重量级的角色,影响了一批新技术的诞生。今天,就让我们来认识下它们,它们的名字,分别叫做 SOAP 和 REST。

概念

SOAP,Simple Object Access Protocol,即简单对象访问协议,定义了数据对象传输的格式,以便在网络的节点之间交换信息。你可能会问,HTTP 不就是干这事的吗?其实,它们都在 OSI 7 层模型的应用层上,但却互不冲突,SOAP 并不依赖于 HTTP 而存在,而且它们可以互相配合。

HTTP 负责信息的传输,比如传递文本数据,关心的是消息的送达,但却不关心这个数据代表着什么。这个数据可能本来是一个内存里的对象,是一篇文章,或者是一张图片。但是 SOAP 恰恰相反,它关心的就是怎样把这个数据给序列化成 XML 格式的文本,在传输到对端以后,再还原回来。

用一个形象的比喻就是,**消息传输就像快递,HTTP 主要关心的是信封,而 SOAP 主要关心的是信封里的物件。**今天我们讨论的 SOAP,不仅仅是协议本身,更主要的是它的风格。

REST,Representational State Transfer,即表现层状态转换,指的是一种为了信息能在互联网上顺利传递而设计的软件架构风格。对,请注意,SOAP 是协议,但 REST 是风格,而非协议或标准,至于 HTTP,它是 REST 风格的重要载体。重要,但不是唯一,因为载体并不只有 HTTP 一个,比如还有 HTML 和 XML,它们恰当地互相配合,组合在一起,来贯彻和体现 REST 的风格。

SOAP 和 REST,由于概念层次上的不同,其实原本是无法放到一起比较的,但是当我们旨在风格层面上讨论 SOAP 和 REST 的时候,这件事却变得可行而有趣了。

现在让我们用一个实际例子来进一步认识它们。这个例子很常见,假设我们要设计一个图书馆,馆中的书可以增删改查(CRUD),特别是要添加一本书的时候,我们分别来看看,应用 SOAP 该怎么做,应用 REST 又该怎么做。

SOAP

这是一个最简单的给图书馆添加一本书的 XML 消息体:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
 xmlns:soap="https://www.w3.org/2003/05/soap-envelope/"
 soap:encodingStyle="https://www.w3.org/2003/05/soap-encoding">
  <soap:Body xmlns:b="...">
    <b:CreateBook>
      <b:Name>...</m:Name>
      <b:Author>...</m:Author>
      ...
    </b:CreateBook>
  </soap:Body>
</soap:Envelope>

让我来简单解释一下:

1.第一行指明了这个消息本身格式是 XML,包括它的版本号和编码格式。

2.这里的很多标签都带有“soap:”的前缀,其实,这里的“soap”就是 XML 的命名空间(其中“xmlns”就是指“xml namespace”),并且通过 XML schema 的方式预先定义好了如下两个 SOAP 消息必须要遵从的规则:

  • 一个是代码片段第 3 行的 soap-envelope,它定义了基本的语法规则,比如标签的从属关系,比如同级某标签的个数限制等等,举例来说,你可以看到例子中有一个 Body 标签位于 Envelope 内部,这就是它定义的;

  • 另一个是代码片段第 4 行的 soap-encoding,它定义了编码和数据类型等规则。

3.在 Body 标签内部,有一个 CreateBook 标签,这是我们的业务标签,命名空间 b 也是我们自己定义的。通过在内部封装姓名(Name)和作者(Author)等书本信息,实现了在图书馆中添加书本的需求。

上面是一个最简单的例子,实际在 Envelope 中还可以添加 Head 标签,用于存放头部信息,在 Body 中可以添加 Fault 标签,用于存放错误信息。关于这些,都在 XML schema 中做了严格的定义,通过它可以帮助验证一个 XML 是否符合格式,从而可以在最短的时间内验证并发现消息中的格式问题。

SOAP 通常是通过 HTTP POST 的方式发送到对端的,这个图书馆对书本的增删改查操作,URL 可以是同一个,这是因为 SOAP 消息的具体内容写明了具体要干什么(上述的 CreateBook 标签)。比如下面这个例子,请注意其中的 Content-Type,它是令响应具备自我描述特性的重要组成部分:

POST /books HTTP/1.1
Host: xxx
Content-Type: application/soap+xml; charset=utf-8
Content-Length: xxx

... (省略前述的 SOAP 消息体)

最后,谈谈经常和 SOAP 放在一起谈论的 WSDL,Web Service Description Language。

WSDL 用于描述一个 Web Service,说白了,就是用来说明某个 Web 服务该怎样使用,有怎样的接口方法,支持怎样的参数,会有怎样的返回。由于支持 SOAP 的服务端接口是经常使用 WSDL 来描述,因此我们才看到它们总被放在一起讨论,于是在这种情况下,WSDL 常常被形容成 SOAP 服务的使用说明书,但是请注意,本质上它们之间不存在依赖关系。

这种将服务和服务的描述解耦开设计的方式非常普遍,希望你可以去类比和联想。在软件的世界里,我们经常谈论这个“描述”的行为,以及描述者和被描述者。比如元属性描述数据,方法签名描述方法,类描述对象等等。

REST

现在,我们再来看 REST 的做法。**REST 的核心要素包括资源、表现层和状态转换这三个部分。**我们把前面客户端发送请求的过程使用 REST 风格再来实现一遍,你将看到这三个要点是怎样体现出来的:

1. 协议

我们将使用 HTTP 协议,在加密的情况下,协议是 HTTPS,但这对我们的实现来说没有什么区别。

2. URL

通常来说,这个 URL 要包括域名、版本号以及实体名称,而这个 URL 整体,代表了 REST 中的一类或一项“资源”。比如说:

https://xxx/v1/books

请注意其中的实体名称,它往往是一个单纯的名词,并且以复数形式出现。

这里提到了 URL,我想给经常混用的 URL、URI 做个简要的说明:URL 指的是 Uniform Resource Locator,URI 指的是 Uniform Resource Identifier,前者是统一资源定位符,后者是统一资源标识符。Identifier 可以有多种形式,而 locator 只是其中一种,因此 URI 更宽泛,URL 只是 URI 的其中一种形式

当我们提到一个完整的地址,例如 https://www.google.com, 它就是 URL,因为它可以被“定位”,它当然也是 URI;但是如果我们只提到上面地址的一个片段,例如 www.google.com,那么由于缺少了具体协议,我们无法完成完整的定位,因此这样的东西只能被叫做一个标识符,故而只能算 URI,而非 URL。

3. 方法

HTTP 的方法反映了这个接口上将执行的行为,如果用自然语言描述,它将是一个动词。比如说,给图书馆添加一本图书,那么这个行为将是“添加”。在以 REST 风格主导的设计中,我们将使用这样的 HTTP 方法来代表增删改查(CRUD)的不同动作:

重点解释下表格的最后两列:

  • **幂等性指的是对服务端的数据状态,执行多次操作是否和执行一次产生的结果一样。**从表格中你可以看到,创建资源单位不是幂等的,执行多次就意味着创建了多个资源单位,而其它操作都是幂等的。通常来说,幂等操作是可以被重试而不产生副作用的。
  • **安全性指的是该操作是否对服务端的数据状态产生了影响。**从表格中可以看到,获取资源的操作是安全的,不会对资源数据产生影响,但是其它操作都是不安全的。一定条件下,安全操作是可以被缓存的,而不安全的操作,必定对服务端的状态产生了影响,这体现了 REST 的“状态转换”这一要素。

全栈系统的设计和优化都需要紧密围绕幂等性和安全性进行,这是两个非常重要的概念,在我们后续的学习中,你还会反复见到它们,并和它们打交道。

你看,通过这样的办法,就把 HTTP 的方法和实际对资源的操作行为绑定起来了。当然,还有一些其它方法,比较常见的有:

  • PATCH:和 PUT 类似,也用于资源更新,但支持的是资源单位的部分更新,并且在资源不存在的时候,能够自动创建资源,这个方法实际使用比较少。
  • HEAD:这个方法只返回资源的头部,避免了资源本身获取和传输的开销。这种方法很有用,经常用来检查资源是否存在,以及有无修改。
  • OPTIONS:这个方法常用来返回服务器对于指定资源所支持的方法列表。

4. 正文

POST 和 PUT 请求都是有 HTTP 正文的,正文的类型和 Content-Type 的选取有关,比如 JSON 就是最典型的一种格式。请不要轻视这里的 Content-Type,从本质上说,它代表了资源的表现形式,从而体现了 REST 定义中的“表现层”这一要素。

最后,回到我们实际的图书馆添加图书的问题。SOAP 添加一本书的消息,用 REST 风格的 POST 请求实现就会变成这样:

POST /v1/books HTTP/1.1
HOST: ...
Content-Type: application/json

{
  "name": "...",
  "author": "...",
  ...
}

风格之争

看到这儿,你应该已经感受到了,SOAP 和 REST 代表了两种迥异的风格。在我们取舍技术的时候,如果没有给出具体场景和限制,我们往往是很难讲出谁更“好”,而是需要进行比较,权衡利弊的。

SOAP 明显是更“成熟”的那一个。它在数据传输的协议层面做了更多的工作,藉由 XML schema,它具备更严格的检查和校验,配合 WSDL,在真正发送请求前,几乎就对所有远程接口事无巨细的问题有了答案。但是,它的复杂度令人望而生畏,也是它最受人诟病的地方。

REST 则相反,新接口的学习成本很低,只需要知道资源名称,根据我们熟知的规约,就可以创建出 CRUD 的请求来。但是直到真正发送请求去测试为止,并没有办法百分百确定远程接口的调用是否能工作,或者说,并不知道接口定义上是否有不规范、不合常规的坑在里面。

对于互联网来说,SOAP 已经是一项“古老”的技术了,晚辈 REST 似乎更切合互联网的潮流。在大多数情况下,REST 要易用和流行得多,于是很多人都不喜欢繁琐的 SOAP 协议。技术的发展总是有这样的规律,一开始无论问题还是办法都很简单,可是随着需求的进一步增加,解决的方法也缓慢演化,如 SOAP 一般强大而复杂,直到某一天突然掉到谷底,如 REST 一般返璞归真。

但是别忘了,有利必有弊。首先,正是因为 REST 只是一个缺乏限制的风格,而非一个严谨的规范,有太多不明确、不一致的实现导致的问题,这些问题轻者给接口调用者带来困惑,重者导致接口调用错误,甚至服务端数据错误。

其次,REST 通过 HTTP 方法实现本身,也受到了 HTTP 方法的局限性制约。比如最常见的 GET 请求,有时需要一个复杂的查询条件集合,因此参数需要结构化,而 GET 只支持一串键值对组合的参数传递,无法适应业务需要。对于这样的问题,有一些 workaround,比如使用 POST 消息体来传递查询条件的结构体,但那已经偏离了 REST 的最佳实践,丢失了 GET 本身的优势,也带来了实现不一致等一系列问题。

最后,REST 还存在的一个更本质的问题,资源是它的核心概念,这原本带来了抽象和简洁的优势,但如今也成为了它的桎梏。或者说,前面反复提到的增删改查是它最拿手的本事,可是互联网的需求是千变万化的,远不只有简单的增删改查。有时需要一个复杂的多步操作,有时则需要一个异步事务(需要回调或者二次查询等等方式来实现),这些都没有一个完美统一的 REST 风格的解决方案。即便实现出来了,也可谓五花八门,同时失去了以往我们所熟知的 REST 的简洁优势。

**互联网总在变复杂,但矛盾的是,人们却希望互联网上的交互会不断变简单。**于是这引发了 REST 的流行,可即便 REST 再流行,依旧有它不适合的场景;SOAP 虽古老,依然有它的用武之地。

对于全栈工程师或者期待修炼全栈技能的你我来说,trade-off 是永恒的话题。另外,除了 SOAP 和 REST,其实我们还有其它选择。我将在下一讲,结合实例具体介绍如何选择技术,并设计和实现远程接口。

总结思考

今天我们认识并学习了 SOAP 和 REST 这样两种截然不同的风格,前者工整、严谨和细致,后者自由、灵活和简约。两道思考题如下:

  • 在做技术比较的时候,文中已经简单介绍了 REST 和 SOAP 的优劣,你觉得,它们各自适合怎样的业务场景呢?
  • 有位程序员朋友在应用 RESTful 风格设计用户管理系统的接口时,“删除单个用户”功能的 URL 举例如下,你觉得有哪些问题?
http://xxx/deleteUser?userName=James

今天的内容就到这里,希望你已经享受到了技术学习的快乐,如果你还有余力,请继续学习下面的选修课堂和扩展阅读。最后,对于上面的问题,或者你对今天的学习有什么感受,欢迎在留言区和我讨论!

选修课堂:动手调用 RESTful API

学习全栈怎么能不动手实践呢,现在就让我们开始吧。有一些在线工具,预置了 REST 风格的接口服务,我们可以使用命令行去指定不同的 HTTP 方法,发送一些不同的 HTTP 请求,观察返回,通过实际的练习,相信你能够更好地理解 REST。这样的工具有很多,你可以自行搜索,也可以直接选择 REQ | RES

你可以使用网站上预置的请求,但我更推荐你自己写 curl 命令。比如发送一个 GET 请求,列出所有用户:

curl -v https://reqres.in/api/users | jq

其中的 -v 参数可以帮助输出详尽的信息,包括请求和响应的完整信息,当然也可以不用;后面的 “| jq” 是为了让返回的 JSON 数据展示更美观,当然,你需要安装 jq。如果你没有安装,不使用 jq 管道也是完全可以的。

再比如,使用 POST 请求创建一个用户:

curl -X POST -d '{"name":"xxx", "job":"yyy"}' -H "Content-Type: application/json" https://reqres.in/api/users | jq

这里使用了 -x 参数指定其为 POST 请求,之后的 Content-Type 是必不可少的,而 JSON 形式的 user 对象则通过参数 -d 传了过去。

最后得到了这样的结果:

... (省略请求统计信息)
{
  "name": "xxx",
  "job": "yyy",
  "id": "585",
  "createdAt": "2019-07-20T22:19:49.825Z"
}

扩展阅读

  • W3Cschool 上的 SOAP 教程,如果你对 SOAP 不够熟悉,那么你可以参考这个简明扼要的教程。
  • 【基础】REST API Tutorial,REST 的教程很多,这是我觉得非常简洁和清晰的一个。
  • ProgrammableWeb 上的 Web API 列表,排名最靠前的 10 个 API,其中有 9 个的架构风格都是 REST,这也从侧面应证了 REST 在互联网的趋势。
  • REST 和 SOAP:谁更好,或者都好?这是一篇内容精悍的译文,分别介绍了适合 REST 和 SOAP 的场景。

标签:HTTP,04,URL,REST,风格,POST,SOAP
From: https://www.cnblogs.com/rskd/p/18016265/04--gong-zheng-yu-zi-you-de-feng-ge-zhi-zhengsoap

相关文章

  • 【虚拟机新手起步03】3步完成ubuntu22.04安装。
    ubuntu下载安装详细过程一、ubuntu镜像下载二、打开VMware使用ubuntu镜像三、设置ubuntu虚拟机一.ubuntu镜像下载:https://cn.ubuntu.com/download二.打开VMware使用ubuntu镜像这块的话使用root会报错,使用一个别的用户名:启动ubuntu:三.设置ubuntu虚拟......
  • 【性能测试】MYSQL锁和mysql事务问题排查04
    一、MYSQL锁目的:解决客户端并发访问冲突问题查看死锁showOPENTABLESwhereIn_use>0案例登录接口 #锁定表LOCKTABLESlitemall.litemall_userREAD;#睡眠160秒SELECTSLEEP(160);#解锁表UNLOCKTABLES;当用户表被锁定时,接口无法登录访问,解锁后可以正......
  • 代码随想录算法训练营第十七天| 110.平衡二叉树 257. 二叉树的所有路径 404.左叶
    110.平衡二叉树 题目链接:110.平衡二叉树-力扣(LeetCode)思路:判断平衡二叉树,就是判断两个子树的高度差,继而问题转化为了如何求子树的高度——后序遍历(主要卡在了这里)。递归函数返回的是树的高度,同时用-1来表示退出递归(一开始想着用bool型作为返回值,发现函数不好设计)。同时要关......
  • docker 中安装apt-get install vim 失败,且apt-get update 报404
    在docker中安装vim时,安装失败。在更新apt-get时,报错如下:root@a8a94b78ebf0:/#apt-getupdateIgn:1http://deb.debian.org/debianstretchInReleaseIgn:2http://deb.debian.org/debianstretch-up......
  • Gym 104855E Perfect Permutation
    考虑最后对于每个\(i\)是选\(a_i,b_i,c_i\)之中哪一个的序列。通过观察能发现序列去掉\(b\)后满足开头为\(c\)末尾为\(a\)这个序列就是合法的,同时整个序列都为\(b\)也是合法的。首先如果是个合法序列,对于去掉\(b\)后的开头,其余不是\(b\)的下标肯定比其大,所以......
  • day04_操作系统入门
    今日笔记学操作系统基础概念linux系统linux系统(centos)+vmware安装起来(网络配置,磁盘分区)ubuntu安装xshell服务器的远程连接服务器网站的前后端,数据库app的前后端,数据库微信、腾讯微信的服务器移动端设备上,安装的微信客户端在线笔记笔记对运维来说,就是一个宝藏,mar......
  • 力扣递归 深度优先搜索 之 104. 二叉树的最大深度
    给定一个二叉树root,返回其最大深度。二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。 示例1: 输入:root=[3,9,20,null,null,15,7]输出:3示例2:输入:root=[1,null,2]输出:2/** *Definitionforabinarytreenode. *publicclassTre......
  • 一个好用的IDEA插件RestfulTool: 根据url快速定位方法
    前言我们平时使用IDEA进行web开发,URL通常会分开写在Controller的类和方法上,用一个完整的URL很难定位到具体的方法。IDEA的插件RestfulTool,提供了这样的能力,根据完整URL直接定位方法。使用下载插件有很多插件都有此功能,这里我们选择RestfulTool插件。简单使用......
  • Springboot项目中使用Elasticsearch的RestClient
    上一篇介绍了Elasticsearch的入门《5000字详说Elasticsearch入门(一)》,本篇介绍Springboot如何集成使用Elasticsearch。分为3步:配置properties文件、引入pom依赖、配置RestHighLevelClient类。1、选择ES的ClientAPI我们知道Elasticsearch是一款RestfulAPI风格的分布式搜索引擎......
  • 04-xx军事自动登录
    本节目标:实现xx军事网账号的自动登录1.前置知识点在开始实现之前,需要先学一些必备的前置知识。1.1页面刷新抓包在抓包时,如果有页面刷新,之前的请求包就会被清除(无法分析)。1.2表单请求和ajax请求当看到页面上有一个表单时,当输入账号+点击登录/注册提交,数据提交就两种方式:......