Spring Boot进阶:原理、实战与面试题分析
上QQ阅读APP看书,第一时间看更新

4.2.1 HATEOAS和HAL

要使用Spring HATEOAS,我们首先需要理解什么是HATEOAS,而要解释HATEOAS这个概念先要解释什么是超媒体。接下来,我们就从超媒体开始讲起。

1. HATEOAS

我们已经知道什么是多媒体(Multimedia),以及什么是超文本(Hypertext)。其中超文本特有的优势是拥有超链接(Hyperlink)。如果我们把超链接引入到多媒体中,那就得到了超媒体(Hypermedia),因此这里的关键角色还是超链接。从HATEOAS的字面上进行理解,使用超媒体作为应用状态的引擎,指的就是应用的状态变更将由客户端访问不同的超媒体资源来驱动。这个概念听起来有点复杂,让我们通过一个简单的示例来进行理解,如代码清单4-42所示。

代码清单4-42 超媒体示例代码

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

可以看到,上述响应结果包含了链接地址,因此使用该API的客户端就能够自由选择要下载的具体图片。这些链接告知了客户端有哪些可供选择的图片,以及它们的地址在哪里。这样一来,客户端就能够根据不同的场景,做出符合自身需要的选择。而且,如果客户端只需要一种格式的图片,那就无须下载全部三种版本的图片。因此,这种表现形式有很多优势,既减少了网络负载,又提高了客户端的灵活性,更增进了API的可探索性。

HATEOAS的重要性在于打破了客户端和服务器之间严格的契约,使得客户端可以更加智能和自适应,而RESTful服务本身的演化和更新也变得更加容易。我们知道在使用普通的RESTful API时,客户端需要根据服务器提供的相关文档来了解所暴露的资源和对应的操作。而基于HATEOAS的RESTful API可以实现服务端和客户端的解耦,客户端通过服务器提供的资源来智能地发现可执行操作,这就是所谓的自解释Web API。

2. HAL

HATEOAS更多是一种概念,而HAL(Hypertext Application Language,超文本应用语言)是HATEOAS的一种实现方式。与普通的RESTful风格不同,对每个资源,HAL又将其细分成状态(State)、链接(Links)和子资源(Embedded Resource)三个标准部分,如图4-4所示。

097-1

图4-4 HAL模型结构

这里的资源状态是指资源本身固有的属性,链接定义了与当前资源相关的一组资源的链接集合,而子资源则描述当前资源的具体内容,提供嵌套资源的定义。

举例来说,在不使用HAL的场景下,我们设计一个RESTful风格的API一般会采用如代码清单4-43所示的表现形式。

代码清单4-43 RESTful风格请求和响应示例代码

//请求
GET http://api.example.com/users/tianyalan
//响应
Content-Type: application/json
{
    "id": "user1",
    "name": "tianyalan",
    "email": "tianyalan@email.com"
}

而为了让API返回的数据更具有关联性,我们使用HAL+JSON格式,这时候返回的格式就会变成如代码清单4-44所示的表现形式。注意到这里多了_links属性,其中有一个self.href链接指向当前user资源。

代码清单4-44 HAL+JSON风格请求和响应示例代码

//请求
GET  http://api.example.com/users/tianyalan
//响应
Content-Type: application/json
{
    _links: {
        self: {
        href: "/users/tianyalan"
        }
    }
    "id": "user1",
    "name": "tianyalan",
    "email": "tianyalan@email.com"
}

HAL的出现主要弥补了普通JSON格式在API交互中的不足,让JSON更具有自描述性和导航性。同时,我们也注意到Web应用程序是将API组合起来为系统提供服务的开发模式。在组合API时,JSON在格式上缺乏描述性的缺陷就体现得非常明显。我们要为API编写文档,要为API之间的数据关系和交互方式提供详细的说明。而如果使用HAL+JSON来描述如代码清单4-45所示的一个带有location属性的user信息,我们就可以很清晰地知道user信息和location信息的来源,这些信息都在子资源中有所体现。

代码清单4-45 带有location属性的HAL+JSON风格请求和响应示例代码

//请求
GET  http://api.example.com/users/tianyalan
//响应
Content-Type: application/json
{
    _links: {
        self: {
            href: "/users/tianyalan"
        }
    }
    "id": "user1",
    "name": "tianyalan",
    "email": "tianyalan@email.com"
    _embedded: {
        location: {
        _links: {
            self: {
                href: 'http:// api.locationservices.com/locations/1'
            }
        },
        id: 1,
        city: 'hangzhou'
        }
    }
}

讲到这里,你可能会好奇,我们应该使用什么框架来开发类似这样的Web API呢?Spring HATEOAS就是这样一个功能强大的开发框架。事实上,Spring Boot框架内部也大量使用了Spring HATEOAS来对外暴露自解释的Web API。这方面典型的案例就是提供系统监控功能的Spring Boot Actuator组件,我们在本书第12章中会有进一步介绍。