KeyPointsHATEOAS是HypertextAsTheEngineOfApplicationState的缩写。在Richardson成熟度模型中,它是REST的第一种形式。单页应用程序正变得越来越流行。前后端分离的开发模式进一步细化了分工,但也引入了大量的重复性工作。例如,当一些业务规则必须在后端实现时,前端也需要重新实现。再次实施它以获得更好的用户体验。虽然HATEOAS并不是消除这些重复的唯一方法,但作为一种架构原则,它让团队更容易找到消除重复的“套路”。什么是HATOEASHATEOAS是HypertextAsTheEngineOfApplicationState的缩写。除了在响应(response)中返回资源本身,使用Hypermedia的API还会返回一组额外的Links。这组链接描述了消费者下一步可以做什么以及如何为资源做这件事。例如,假设向API发起get请求,获取指定订单的资源表示,那么应该是这样的:HTTP/1.1200OKServer:Apache-Coyote/1.1Content-Type:application/hal+json;charset=UTF-8Transfer-Encoding:chunkedDate:Fri,05Jun201502:54:57GMT{"tracking_id":"123456","status":"WAIT_PAYMENT","items":[{"name":"potato","quantity"":1}],"_Links":{"self":{"href":"http://localhost:57900/orders/123456"},"cancel":{"href":"http://localhost:57900/orders/123456"},"payment":{"href":"http://localhost:57900/orders/123456/payments"}}}了解Link中“self”的消费者都知道使用get方法访问其“href”uri以查看订单的详细信息。了解Link中“取消”的消费者都知道,使用delete方法访问其“href”uri可以取消订单。了解Link中“付款”的消费者知道,使用post方法访问他们的“href”uri可以为该订单付款。REST是目前业界非常流行的名词。好像发布的API没有REST前缀,不好意思跟别人打招呼。然而,大多数声称是REST的API实际上并没有达到Richardson成熟度模型的第三级:超媒体。而REST的领袖RoyFielding博士甚至说HATEOAS是REST的前提。这不是一个选择。如果没有超媒体,它就不是REST。(摘自Infoq对菲尔丁博士的二次采访)那么HATOEAS带来了哪些优势呢?一个明显的好处是,只要客户端一直使用LinkRel来获取URI,服务器就可以在不破坏客户端的情况下实现URI。下一步实现对URI的修改,从而进一步解耦客户端和服务端。另一个容易被忽视的优势是它可以帮助客户端开发人员探索API。Links实际上是在提示开发者接下来可以进行什么样的业务操作。开发人员虽然精通技术,但往往对业务知之甚少。这些提示可以帮助他们了解业务,至少是查询API文档的良好起点。想象一下,如果一个API的响应中有一个新的Link,敏感的开发者可能会问这个Link是做什么用的。这是一个新功能吗?虽然看似不起眼,但往往能让两个团队的成员更容易沟通。单页应用和HATEOAS在过去的几年里,WEB开发技术发生了很多重大变化,其中之一就是单页应用,它往往能带来更流畅的用户体验。在这个领域,分工进一步细化。前端工程师专注于客户端程序构建和HTML、CSS等效果的开发,而后端工程师更侧重于高并发、DevOps等技能。大多数功能需要前后端工程师的配合。可能有人会质疑,为什么不做全栈工程师呢?诚然,如果一个人能够交付端到端的特性,自然会降低沟通成本,但全栈工程师并不好找,只有细化分工才能适应大规模的开发模式.继Ajax之后,单页应用和前后端分离架构进一步催生了大量的API。我们迫切需要一些方法来管理这些API的开发和演进,而HATEOAS应该在这里占有一席之地。在摸索中前行,自由重命名你的资源我们常说敏捷开发要拥抱变化。因此,重构、单元测试、持续集成等技术在敏捷开发中受到推崇,因为它们可以让变更变得更容易、更安全。HATOEAS也是这样的技术。想象一下,在项目初期,团队对业务了解不深,很可能会想出错误的业务术语,或者业务对象的建模不完全合适。反映在API上,你可能希望能够修改API的URI。在非HATOEAS项目中,由于URI是在客户端硬编码的,即使你设计得很漂亮(准确的HTTP动词、复数命名的资源、禁止动词等),也不帮你更方便地修改它们,因为你的重构需要前端开发人员的配合,他/她不得不停下手头的其他工作。但是在采用HATEOAS的项目中,这很容易,因为客户端是通过Link找到API的URI,所以可以在不破坏APIScheme的情况下修改它的URI。当然,你不能保证所有的APIURI都是通过Links获取的。需要安排一些RootResource,比如/api/currentLoggedInUser,否则客户端无法发起第一次请求。HTTP/1.1200OKPath:/api/currentLoggedInser(1){...//omittedcontent"_Links":{(2)"searchUserStories":{"href":"http://localhost:8080/userStories/search{?页面,大小}"},"searchUsers":{"href":"http://localhost:8080/users/search{?page,大小,用户名}"},"注销":{"href":"http://localhost:8080/logout"}}}RootResource,它们是API的入口,客户端可以通过它们浏览当前用户可以访问哪些资源。可以定义多个RootResource,并保证它们的URI不会改变Link引入的URI可以随意改变,可能是因为需要重命名资源,或者需要抽取新的服务(域名改变)来消除重复业务规则验证实现,更容易适应变化经验告诉我们,客户是不可信任的,因此在服务端,我们需要根据业务规则来验证当前请求是否合法。这样可以确保业务是正确的,但是在用户发起请求后告诉用户请求失败有时会令人沮丧。为了用户体验,可能需要根据业务规则显示一些组件。例如,对于一个业务对象,要求只有当前用户可以编辑时才显示编辑按钮。传统服务端渲染架构下,验证码一般可以复用,但在单页应用中,往往由于技术栈不同,代码无法直接共享,业务规则前后台实现一次分别结束。例如,在我们最近的一个项目中,前端和后端分别实现了以下规则:给定一个用户故事,只有它的作者可以编辑它。服务端通过在用户故事的API中暴露作者,帮助前端完成编辑按钮的条件渲染。.HTTP/1.1200OKPath:/api/userStories/123{"author":"john.doe@gmail.com"(1)}1.与当前用户比较判断是否渲染编辑按钮,但是如果规则改变,前端和前端都需要适配这个变化,所以我们用HATEOAS重构了一下:HTTP/1.1200OKPath:/api/userStories/123{"author":"john.doe@gmail.com","_links":{"updateUserStory":{"href":"http://localhost:8080/api/userStories/123"(1)}}}2、现在前端会验证当前用户是否有权限根据updateUserStory链接是否出现来编辑用户故事。后来业务规则变了,因为除了作者,系统管理员也可以编辑用户故事。这个时候只有后端需要响应这个变化。您可能怀疑这也可以通过公开用户故事的计算属性isCurrentLoggedInUserAvailableToUpdate来完成。是的,HATOEAS不是唯一的方式,但作为一种架构约束,团队自然会想到,而计算属性则需要团队成员有更强的抽象能力。总结HATEOAS提倡在响应中返回Link,提示对资源进行下一步操作。这种方法解耦了服务器端URI,并使客户端开发人员更容易探索API。***,通过Link判断业务状态也可以有效杜绝单页应用中业务规则的重复执行。【本文为专栏作者“ThoughtWorks”原创稿件,微信公众号:Thinkworker,转载请联系原作者】点此查看该作者更多好文
