zoukankan      html  css  js  c++  java
  • REST(Representational state transfer)的四个级别以及HATEOAS介绍

    Rest

    RES(Representational state transfer):表现层状态转移。其实它省略了主语,「表现层」其实指的是「资源」的「表现层」,所以通俗来讲就是:资源在网络中以某种表现形式进行状态转移

    分解开来:

    • Resource:资源,即数据。比如newsfeed,friends,order等;
    • Representational:某种表现形式,比如用JSON,XML,JPEG等;
    • State Transfer:状态变化。通过HTTP动词实现

    总结起来就是将数据(资源)转换成多种格式用以使用,这样的话就可以针对不同的需求提供不同的数据(资源)格式?非常方便

    注意:REST是一种架构设计风格而不是标准,如果一个架构符合REST原则,我们就称它为Restful架构,所谓架构风格就是凌驾于架构之上的一组约束

    Restful

    下面我们来理解一个具体的Restful架构——面向资源的架构(Resource-Oriented Architecture,ROA):

    • 资源是由URI来指定。所谓「上网」,就是与互联网上一系列的「资源」互动,调用它的URI。
    • 对资源的操作包括CRUD(获取、创建、修改和删除)资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
    • 通过操作资源的表现形式来操作资源。具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定。
    • 资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。

     

    Rest风格的使用

    在设计web接口的时候,REST主要是用于定义接口名,接口名一般是用名词写,不用动词,那怎么表达“获取”或者“删除”或者“更新”这样的操作呢——用请求类型来区分。

    比如,我们有一个friends接口,对于“朋友”我们有增删改查四种操作,怎么定义REST接口?

    • 增加一个朋友,uri: generalcode.cn/v1/friends 接口类型:POST
    • 删除一个朋友,uri: generalcode.cn/va/friends 接口类型:DELETE
    • 修改一个朋友,uri: generalcode.cn/va/friends 接口类型:PUT
    • 查找朋友,uri: generalcode.cn/va/friends 接口类型:GET

    上面我们定义的四个接口就是符合REST协议的,请注意,这几个接口都没有动词,只有名词friends,都是通过Http请求的接口类型来判断是什么业务操作。

    举个反例:generalcode.cn/va/deleteFriends 该接口用来表示删除朋友,这就是不符合REST协议的接口。

    一般接口的返回值是JSON或者XML类型的,应该JSON类型偏多

    但是,REST开发将对资源的操作不仅仅局限于CRUD(创建、获取、修改、删除)的语义(即,将对资源的CRUD操作映射到 GET/POST/PUT/DELETE四个HTTP方法)。如果仅仅局限于这里其实收窄了REST的适用范围。甚至还有很多仅仅使用了HTTP,而没有使用SOAP的Web服务API,都自称是REST风格(RESTful)的API,应该不太正确吧。。?

    So,什么才是真正的REST风格,REST的创造者Fielding曾发博:REST APIs must be hypertext-driven!(REST API必须是超文本驱动的!),超文本驱动这个理念变成了一个缩写词HATEOAS,这个缩写词来自于当初Fielding博士论文中的一句话: hypermedia as the engine of application state(将超媒体作为应用状态的引擎),其实超文本驱动(Hypertext Driven)的理念才是REST架构风格最核心的理念,也是REST风格的架构达到松耦合目标的根本原因。

    Restful的四个级别

    下面我们用一个不是那么规范的模型来理解restful各个级别:

    Level 0 - 面向前台

    我们在咖啡店向前台点了一杯拿铁,这个过程可以用这段文字来描述

    {
        "addOrder": {
            "orderName": "latte"
        }
    } 

    我们通过这段文字,告诉前台,新增一笔订单,订单是一杯拿铁咖啡,接着,前台给我们返回这么一串回复:

    {
        "orderId": "123456"
    }

    假设我们有一张会员卡,我们想查询一下这张会员卡的余额,这时候,要向前台发起另一个询问:

    {
        "queryBalance": {
            "cardId": "4470313335"
        }
    }

    查询卡号为886333的卡的余额,查询的结果返回来了:

    {
        "balance": "0"
    }

    没钱…… 
    哈哈,没钱,现在我们要跟前台说,这杯咖啡不要了:

    {
        "deleteOrder": {
            "orderId": "123456"
        }
    }

    Level1面向资源

    现在这家咖啡店越做越大,来喝咖啡的人越来越多,单靠前台显然是不行的,店主决定进行分工,每个资源都有专人负责,我们可以直接面向资源操作。 
    比如还是下单,请求的内容不变,但是我们多了一条消息:

    /orders
    {
        "addOrder": {
            "orderName": "latte"
        }
    }

    多了一个斜杠和orders,这是什么意思? 
    这个表示我们这个请求是发给哪个资源的,订单是一种资源,我们可以理解为是咖啡厅专门管理订单的人,他可以帮我们处理所有有关订单的操作,包括新增订单、修改订单、取消订单等操作。 
    接着还是会返回订单的编号给我们:

    {
        "orderId": "123456"
    }

    下面,我们还是要查询会员卡余额,这次请求的资源变成了cards:

    /cards
    {
        "queryBalance": {
            "cardId": "447031335"
        }
    }

    接下来是取消订单:

    /orders
    {
        "deleteOrder": {
            "orderId": "123456"
        }
    }

    Level2 - 打上标签

    接下来,店主还想继续优化他的咖啡厅的服务流程,他发现负责处理订单的员工,每次都要去订单内容里面看是新增订单还是删除订单,还是其他的什么操作,十分不方便,于是规定,所有新增资源的请求,都在请求上面写上大大的‘POST’,表示这是一笔新增资源的请求。 
    其他种类的请求,比如查询类的,用‘GET’表示,删除类的,用‘DELETE’表示,修改用PATCH表示。 
    来,我们再来重复上面那个过程,来一杯拿铁:

    POST /orders
    {
        "orderName": "latte"
    }

    请求的内容简洁多啦,不用告诉店员是addOrder,看到POST就知道是新增,返回的内容还是一样:

    {
        "orderId": "123456"
    }

    接着是查询会员卡余额,这次也简化了很多:

    GET /cards
    {
        "cardId": "447031335"
    }

    这个请求我们还可以进一步优化为这样:

    GET /cards/447031335 

    直接把要查询的卡号写在后面了。 

    Level 3 - 完美服务

    忽然有一天,有个顾客抱怨说,他买了咖啡后,不知道要怎么取消订单,咖啡厅一个店员回了一句,你不会看我们的宣传单吗,上面不写着

    DELETE /orders/{orderId}

    顾客反问道,谁会去看那个啊,店员不服,又说到,你瞎了啊你……后面两人吵着吵着还打了起来… 
    噗,真是悲剧… 
    有了这次教训,店长决定,顾客下了单之后,不仅给他们返回订单的编号,还给顾客返回所有可以对这个订单做的操作,比如告诉用户如何删除订单。现在,我们还是发出请求,请求内容和上一次一样:

    POST /orders
    {
        "orderName": "latte"
    }

    但是这次返回时多了些内容:

    {
        "orderId": "123456",
        "link": {
            "rel": "cancel",
            "url": "/order/123456"
        }
    }

    这次返回时多了一项link信息,里面包含了一个rel属性和url属性,rel是relationship的意思,这里的关系是cancel,url则告诉你如何执行这个cancel操作,接着你就可以这样子来取消订单啦:

    DELETE /orders/123456

    哈哈,这服务真是贴心,以后再也不用担心店员和顾客打起来了。 
    Level3的Restful API,给使用者带来了很大的便利,使用者只需要知道如何获取资源的入口,之后的每个URI都可以通过请求获得,无法获得就说明无法执行那个请求。

     看了易懂的例子,下面我们回到原理:

    第一级:在架构中引入资源(Resource)的概念。

    大多数WS-*服务和POX都只是使用一个URI作为一个服务端口,也只使用一个HTTP方法传输数据。这种做法相当于把HTTP这个应用层协议降级为传输层协议用,《REST实战》也一再强调HTTP是一种应用协议而不是传输协议。再好一点就是使用多个URI,然而不同的URI只是作为不同的调用入口,与此同时只使用同一个HTTP方法传输数据。最常见的错误就是在URI中包含动词,比如URI http://example.com/getOrder?orderId=1234,其实「资源」表示一种实体,所以应该是名词,动词应该放在HTTP协议中。而与此同时URI也有可能破坏HTTP GET的安全性和幕等性,比如某个客户端在http://example.com/updateOrder?id=1234&coffee=latte上执行GET(而不是POST),就能创建一笔新的咖啡订单(一个资源),按理来说GET请求不能改变服务的任何状态。

    第二级:每一个URI代表一种资源,支持HTTP动词。

    此时使用多个URI的话,需要让不同的URI代表不同的资源(注意多个URI可能指向同一个Resource,而一个URI不能指向不同Resource。),同时使用多个HTTP方法操作这些资源,例如使用POST/GET/PUT/DELET分别进行CRUD操作。这时候HTTP头和有效载荷都包含业务逻辑,例如HTTP方法对应CRUD操作,HTTP状态码对应操作结果的状态。我们现在看到的大多数所谓RESTful API做到的也就是这个级别。《REST实战》的译者也谈到:悟性差的人,理解到CRUD式Web服务就满足了。而悟性好的人,可以彻底理解超文本驱动,甚至是与REST关系密切的语义网,最终达到 REST开发的最高境界。

    第三级:HATEOAS,使用超媒体(hypermedia)作为应用状态引擎。

    根据Roy的严格规定,超媒体(hypermedia)是REST的先决条件。任何其他东西不应该自我标榜为REST。要解释HATEOAS这个概念先要解释什么是超媒体:我们已经知道什么是多媒体(multimedia),以及什么是超文本(hypertext)。其中超文本特有的优势是拥有超链接(hyperlink)。如果我们把超链接引入到多媒体当中去,那就得到了超媒体,因此关键角色还是超链接。使用超媒体作为应用引擎状态,意思是应用引擎的状态变更由客户端访问不同的超媒体资源驱动。

    让我们来看个实例,这个响应内容可能略有不同:

    GET https://api.example.com/profile
    
    {
      "name": "Steve",
      "picture": {
        "large": "https://somecdn.com/pictures/1200x1200.png",
        "medium": "https://somecdn.com/pictures/100x100.png",
        "small": "https://somecdn.com/pictures/10x10.png"
      }
    }

    由于在响应中包含了链接地址,因此使用该API的客户端就能够自由选择要下载怎样的信息。这些链接告知了客户端有哪些选择,并且它们的地址在哪里。因此在这里我们无需同时返回三个不同版本的用户档案图片,我们所做的只是告诉客户端有三种可用的图片尺寸可以选择,并且告诉客户端能够在哪里找到这些图片。这样一来,客户端就能够根据不同的场景,做出符合自身需要的选择。而且,如果客户端只需要一种格式的图片,那就无需下载全部三种版本的图片了。这样一来可谓一箭三雕:既减少了网络负载,又增进了客户端的灵活性,更增进了API的可探索性。

    超媒体的核心概念就是所谓的<link>元素,而这些相互链接的资源实际上描述了一个协议,即引导我们达成某个目标的一系列步骤,例如订购一杯咖啡所需要的点单、付款、取咖啡等等。这就是超媒体的本质:经由资源之间的链接,我们改变整个应用的状态,即超媒体转换了分布式应用的状态。需要注意的是,服务器和消费者两者间交换的是资源状态的表述,而不是应用的状态,被转移的表述中包括了反应应用状态的链接。

  • 相关阅读:
    SQL语句中的左连接、右连接、内连接的理解心得
    MySQL+Java使用心得(1)
    【转】国内常见WEB安全扫描产品概述
    [C puzzle book] operators
    【Python】Symbol Review
    A function for new storage space of string
    【SRX】折腾了半天终于我的那对SRX210 升级到了 12.1R1.9
    [C puzzle book] Control Flow
    [C puzzle book] types
    荷兰TAC的需求
  • 原文地址:https://www.cnblogs.com/vision-xixi/p/10663446.html
Copyright © 2011-2022 走看看