单页应用的HATEOAS实战

开发 开发工具 前端
HATEOAS提倡在响应返回Link来提示对该资源接下来的操作。这种方式解耦了服务端URI,也可以让客户端开发者更容易地探索API。

要点

HATEOAS是Hypertext As The Engine Of Application State的缩写。在 Richardson Maturity Model中, 它是REST的***级形态。

单页应用正越来越受到欢迎,前后端分离的开发模式进一步细化了分工,但同时也引入了不少重复的工作,例如一些业务规则在后端必须实现的情况下,前端也需要再实现一遍以获得更好的用户体验。HATEOAS虽然不是唯一消除这些重复的方法,但作为一种架构原则,它更容易让团队找到消除重复的“套路”。

[[241496]]

什么是HATOEAS

HATEOAS是Hypertext As The Engine Of Application State的缩写。采用Hypermedia的API在响应(response)中除了返回资源(resource)本身外,还会额外返回一组Link。 这组Link描述了对于该资源,消费者(consumer)接下来可以做什么以及怎么做。

举例来说,假设向API发起一次get请求,获取指定订单的资源表述(representation),那么它应该长得像这样:

  1. HTTP/1.1 200 OK 
  2. Server: Apache-Coyote/1.1 
  3. Content-Type: application/hal+json;charset=UTF-8 
  4. Transfer-Encoding: chunked 
  5. Date: Fri, 05 Jun 2015 02:54:57 GMT 
  6.  
  7.   "tracking_id": "123456", 
  8.   "status": "WAIT_PAYMENT", 
  9.   "items": [ 
  10.     { 
  11.        "name": "potato", 
  12.        "quantity": 1 
  13.     } 
  14.   ], 
  15.   "_Links": { 
  16.     "self": { 
  17.       "href": "http://localhost:57900/orders/123456" 
  18.     }, 
  19.     "cancel": { 
  20.       "href": "http://localhost:57900/orders/123456" 
  21.     }, 
  22.     "payment": { 
  23.       "href": "http://localhost:57900/orders/123456/payments" 
  24.      } 
  25.   } 
  • 理解Link中的“self”的消费者知道使用get方法访问其“href”的uri可以查看该订单的详细信息。
  • 理解Link中的“cancel”的消费者知道使用delete方法访问其“href”的uri可以取消该订单。
  • 理解Link中的“payment”的消费者知道使用post方法访问其“href”的uri可以为该订单付款。

REST是目前业界相当火热的术语,似乎发布的API不带个REST前缀,你都不好意思和别人打招呼了。 然而大部分号称REST的API实际上并没有达到Richardson成熟度模型的第三个级别:Hypermedia。 而REST的***Roy Fielding博士更是直言HATEOAS是REST的前提, 这不是一个可选项,如果没有Hypermedia,那就不是REST。(摘自Infoq对Fielding博士的第二段访谈)

[[241497]]

那么HATOEAS带来了什么优势?

一个显而易见的好处是,只要客户端总是使用Link Rel来获取URI,那么服务端可以在不破坏客户端实现的情况下实现URI的修改,从而进一步解耦客户端和服务端。

另一个容易被忽视的优势是它可以帮助客户端开发者探索API,Links实际上提示了开发者接下来可以进行何种业务操作,开发者虽然精通技术,但往往对于业务不甚了解,这些提示可以帮助他们理解业务,至少是一个查询API文档的好起点。想象一下,如果某个API的响应中多了一个新的Link,敏感的开发者可能就会询问这个Link是用来做什么的,是一个新的特性吗?虽然看起不起眼,但这往往使两个团队的成员沟通起来更容易。

单页应用和HATEOAS

在过去的几年里,WEB开发技术发生了很多重大的变革,其中之一就是单页应用,它们往往能带来更平滑的用户体验。在这一领域,分工进一步细化,前端工程师专精客户端程序构建和HTML、CSS等效果的开发,后端工程师则更偏重高并发、DevOps等技能,大部分特性需要前后端工程师配合完成。

或许有人会质疑,为什么不是全栈工程师?诚然,如果一个人就能端到端的交付特性,那自然会减少沟通成本,但全栈工程师可不好找,细化分工才能适应规模化的开发模式。继Ajax之后,单页应用和前后端分离架构进一步催生了大量的API,我们急需一些方法来管理这些API的开发和演进,而HATEOAS应该在此占有一席之地。

[[241498]]

在摸索中前进,自由地重命名你的资源

我们常说在敏捷开发中,应该拥抱变化。所以敏捷开发中推崇重构、单元测试、持续集成等技术,因为它们可以使变化更容易、更安全。HATOEAS也是这样一种技术。想象一下,在项目初始阶段,团队对业务的理解还不深入,很有可能会得出错误的业务术语命名,或者业务对象的建模也不完全合适。反映在API上,可能你希望能够修正API的URI,在非HATOEAS的项目中,由于URI是在客户端硬编码的,即使你把它们设计的非常漂亮(准确的HTTP动词,以复数命名的资源,禁止使用动词等等),也不能帮助你更容易地修改它们,因为你的重构需要前端开发者的配合,而他/她不得不停下手头的其他工作。

但在采用了HATEOAS的项目中,这很容易,因为客户端是通过Link来查找API的URI,所以你可以在不破坏API Scheme的情况下修改它的URI。当然,你不可能保证所有API的URI都是通过Link来获取的,你需要安排一些Root Resource,例如 /api/currentLoggedInUser,否则客户端没有办法发起***次请求。

  1. HTTP/1.1 200 OK 
  2. Path: /api/currentLoggedInser (1) 
  3.  
  4.   …… //omitted content 
  5.  
  6.   "_Links": {  (2) 
  7.     "searchUserStories": { 
  8.       "href": "http://localhost:8080/userStories/search{?page, size}" 
  9.     }, 
  10.  
  11.     "searchUsers": { 
  12.       "href": "http://localhost:8080/users/search{?page, size, username}" 
  13.     }, 
  14.  
  15.     "logout": { 
  16.       "href": "http://localhost:8080/logout" 
  17.     } 
  18.  
  19.   } 
  • Root Resource,它们是API的入口,客户端通过他们浏览当前用户有哪些资源可以访问,你可以定义多个Root Resource,并确保它们的URI不会改变
  • Link引入的URI可以自由地变化,可能是因为需要重命名资源,也可能是需要抽取出新的服务(域名变化)

[[241499]]

消除重复的业务规则校验实现,更容易得适应变化

经验告诉我们,不能相信客户端的请求,所以在服务端我们需要根据业务规则校验当前的请求是否合法。这样确保了业务正确,但当用户发起了请求后才告诉他们请求失败,有时候是一件令人沮丧的事情。为了用户体验,可能会要求某些组件根据业务规则展示。例如,对于某个业务对象,要求编辑按钮只在当前用户可以编辑的情况下才展示。在传统的服务端渲染架构下,一般都可以复用校验的代码,而在单页应用中,往往由于技术栈不同,代码无法直接共用,业务规则在前后端都分别实现了一次。例如,在我们最近的一次项目中,前后端分别实现了如下规则:

  • 给定一个用户故事
  • 只有它的作者才能编辑它

服务端通过在用户故事的API中暴露作者帮助前端完成编辑按钮的有条件渲染。

  1. HTTP/1.1 200 OK 
  2. Path: /api/userStories/123 
  3.  
  4.   "author": "john.doe@gmail.com"  (1) 
  5.  

1. 与当前用户比较判断是否渲染编辑按钮

但如果规则发生变化,前后端都需要适应这一改变,所以我们用HATEOAS重构了一下:

  1. HTTP/1.1 200 OK 
  2. Path: /api/userStories/123 
  3.  
  4.   "author": "john.doe@gmail.com", 
  5.   "_links": { 
  6.  
  7.     "updateUserStory": {  
  8.       "href": "http://localhost:8080/api/userStories/123" (1) 
  9.     }   
  10.  
  11.   }  
  12.  

2. 现在前端会根据 updateUserStory link是否出现来验证当前用户是否具有编辑用户故事的能力

后来业务规则变为除了作者之外,系统管理员也可以编辑用户故事,这时候只需要后端去响应这个变化就行了。你可能会质疑,通过为用户故事暴露一个 isCurrentLoggedInUserAvailableToUpdate的计算属性也可以做到。没错,HATOEAS并不是唯一的办法,但作为一种架构约束,团队会自然而然地想到它,而计算属性则要求团队成员有更强的抽象技能。

总结

HATEOAS提倡在响应返回Link来提示对该资源接下来的操作。这种方式解耦了服务端URI,也可以让客户端开发者更容易地探索API。***,通过Link来判断业务状态,还能有效地消除单页应用中的业务规则重复实现。

【本文是51CTO专栏作者“ThoughtWorks”的原创稿件,微信公众号:思特沃克,转载请联系原作者】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2016-11-01 21:02:47

javascriptreact.jsreact-route

2016-11-28 09:13:29

单页Web模板数据

2014-09-19 10:54:47

用户体验单页面

2020-03-27 09:20:00

单页应用程序网页设计SPAs

2020-10-27 12:07:17

DevOps单页应用程序开发

2014-06-26 09:36:02

Angular评论应用

2019-03-13 09:00:00

Web应用SPAJavaScript

2015-03-30 14:53:12

HTML6JavaScript一片哗然

2014-09-09 10:49:59

AngularJS单页应用

2022-01-05 14:02:31

前端Nginx单页加载

2013-10-23 15:30:31

设计网站

2011-05-07 16:03:56

单页网站网站设计

2011-10-28 11:11:45

应用设计移动设备

2016-09-07 15:35:06

VueReact脚手架

2020-12-02 08:43:00

Flink SQLHBase场景

2011-04-21 15:55:13

彩激光打印喷墨打印

2017-03-06 17:56:20

webpack管理多页应用

2024-04-09 09:24:13

2022-05-05 09:31:34

Go语言漏洞

2014-09-16 10:13:27

Spark搭建环境
点赞
收藏

51CTO技术栈公众号