虽然严格定义的API对那些旨在被其他程序访问的接口是有效的,但花点时间谈谈关于如何创建一个成功的人机接口的基础知识也很有必要。为此,我们将主要讨论HTML接口,其目的是让终端用户在浏览器中使用它。
我们将讨论的大部分概念都适用于其他类型的人机接口,如图形用户界面或移动应用程序。
HTML技术与RESTful技术高度相关,因为它们在互联网的早期是并行发展的。通常情况下,两者在现代Web应用中是交织在一起的。
传统Web接口的工作方式是基于HTTP请求,只使用GET和POST方法。GET方法能从服务器上检索一个页面,而POST方法则与某些提交数据给服务器的表单搭配使用。
这个是先决条件,因为早期的浏览器只实现了这些方法。虽然现在大多数现代浏览器可以在请求中使用所有的HTTP方法,但这仍然是一种常见的需求,从而可以与旧的浏览器兼容。
虽然比起所有可用的那些方法,这样肯定会带来更多限制,但对于简单的网站界面来说,它也能有效地运转。
例如,一个博客网站被阅读的次数远多于在其上撰写博文的次数,所以读者可以使用大量的GET请求来获取信息,也许还有一些POST请求用于发表评论。删除或更改评论的需求一般都很少,尽管这种需求也可以通过使用POST并结合其他URL来实现。
请注意,浏览器在重试POST请求之前会询问你,因为这并非是幂等操作。
由于这些限制,HTML接口的工作方式与RESTful接口是不一样的,但如果在设计中考虑采用抽象和资源的方法,情况也能得到改善。
例如,博客系统常见的一些抽象如下:
❍所有博文(post),以及相关的评论(comment)。
❍一个包含最新博文的主页面。
❍一个搜索页面,能返回包含某个词或标签的博文。
这与资源中的接口非常相似,只有“评论”和“博文”这两类资源,其以RESTful的方式进行区分,并以同样的方式结合起来。
传统HTML接口的主要局限在于,页面内容的每一点变化都需要刷新整个页面。对于像博客这样的简单应用,这种方式能良好运转,但是针对较复杂的应用则需要采用支持动态内容的系统来实现。
为了给浏览器增加交互性,我们可以添加一些JavaScript代码,从而直接在浏览器呈现的内容上执行修改页面的操作。例如,从一个下拉菜单中选择界面的颜色。
这种操作称为 DOM (Document Object Model,文档对象模型),其呈现内容包括HTML定义的文档,可能还有CSS文档。JavaScript程序可以访问这些呈现的内容,并通过编辑相关参数或添加/删除文档元素来对其进行修改。
在JavaScript程序中,也可以发起独立的HTTP请求,所以我们可以用它来完成特定的调用,以检索数据细节来改善用户体验。
例如,有个输入地址的表单,可以用下拉菜单选择国家。在选择某个国家之后,通过对服务器的调用将检索出该国家所对应的地区,并更新输入选项菜单。例如,如果用户选择美国,则美国所有州的列表将被检索出来,并填入界面上的下拉菜单中供选择;如果用户选择加拿大,则将使用地区和省的列表来代替,如图2-6所示。
图2-6 通过下拉菜单内容的自动匹配来改善用户体验
还有一个在某种程度上实现界面逆向更新的例子,就是可以使用邮政编码来自动确定州。
实际上,网上有一个服务可以检索此信息,地址是https://zippopotam.us/。访问该网站不仅能查询到结果,而且还会以JSON格式返回更详细的信息。
这种类型的调用被称为 异步JavaScript和XML (AJAX)。虽然名字中提到了XML,但这并不是必需的,所有格式的信息都可以进行检索。目前,使用JSON格式乃至纯文本都是非常普遍的。当然还可以使用HTML,所以页面的某个区域可以用来自服务器的信息片段(snippet)来代替,如图2-7所示。
图2-7 使用HTML来替换页面中的区域
纯HTML的内容虽然显得有点不那么优雅,但其实也很有效,所以很普遍的做法是,使用RESTful API来检索这些小的HTML元素,预期数据以JSON格式返回,然后通过JavaScript代码来完成基于DOM的页面修改操作。鉴于这个API的目的并不是要完全取代HTML接口,而是对其进行补充,所以当前RESTful API的功能实现可能并不那么完善。仅使用这些RESTful调用,是无法创造出完整的用户体验的。
其他的应用程序可直接采取API优先(API-first,即以API为中心)的技术路线,并基于此开发完善用户浏览体验。
单页应用程序(single-page App)蕴含的理念很简单,就是打开单个HTML页面并动态地改变其内容。如果有任何新的数据需要,它将通过一个特定的(通常是RESTful)API来访问。
这种应用模式完全剥离了人机接口,可理解为负责显示信息的组件与后端服务的分离。后端只为RESTful API提供服务,而不用关注数据的呈现。
这种方法有时被称为API优先,因为它设计了一个从API到呈现的系统,而不是逆向创建,这是开发需要持续发展的服务时所采用的自然方式。
虽然有一些特定的框架和工具是为这个目标而设计的,比如React或AngularJS,但这种方法主要面临着两个方面的挑战:
❍在单个页面上创建一个成功的人机接口所需的技术能力相当高,即使在有工具的帮助下也是如此。任何非细微的有效界面的呈现都需要维护大量的状态,并处理多个调用。这种情况下很容易出现错误,从而影响到页面的稳定性。传统浏览器采用的是完全独立的页面,会限制每个操作步骤的作用域,这样的方式更容易把握。
请记住,浏览器本身带有一些可能难以避免或取代的界面,例如,点击后退按钮。
❍由于需要事先设计和准备API,因此项目在启动阶段进展会相对比较缓慢。它需要更多的规划和前期准备,即使前、后端并行开发,也会有一定困难。
上述情况使得这种方法通常不会被用在从头开发的新应用中。然而,如果应用程序起源于其他类型的用户接口,如智能手机上的App,那么它就可以利用已经存在的RESTful API来生成一个复制原有功能的HTML接口。
这种方法的主要优点是将应用程序从用户接口中分离出来。当把一个有常规HTML接口的应用程序作为一个小项目来开发时,其风险在于,任何其他的用户接口都会倾向于遵照HTML接口来开发。这样有可能很快就会带来大量的技术负债,并影响到API的设计,因为所使用的抽象概念很可能是从现有的HTML接口衍生出来的,而并不是最合适系统后续发展的。
纯粹的API优先方法很大程度做到了接口分离,所以创建出的新接口和已经存在的API同样易于使用。对于需要多种接口的应用,比如既要支持HTML接口,也要支持iOS和Android这些不同智能手机的App,这种方法也许是一种好的解决方案。
在呈现完整的用户接口方面,单页应用程序也相当具有创新性。它可以生成丰富而复杂的界面,甚至超越传统意义上的“网页”,比如游戏或交互式应用的场景。
正如我们所看到的,系统完全基于单页应用程序来开发可能是相当具有挑战性的。从某种程度上来说,这种方式是让浏览器来完成相关业务。
所以通常情况下,在进行系统设计时不会采取这么极端的方式,而是创造一个更为传统的Web界面。这种界面依然会被当作Web应用程序,但它在很大程度上依赖于JavaScript,并使用RESTful接口获取信息。可以将其作为从传统的HTML接口过渡到单页应用程序的一个顺理成章的步骤,但也可能是一个有意识的决定。
这种方法结合了前述的两种模式。一方面,它仍然需要HTML接口来呈现界面上常规性的内容,提供清晰的页面供浏览。另一方面,它创建了RESTful API来填充大部分的信息,并使用JavaScript来调用该API。
这种方法与动态页面的方法类似,但有一个重要的区别,那就是其目的在于设计一系列关联的API,可以不必遵照HTML接口来使用。这种实现方法是完全不一样的。
在具体实践过程中,这种方法往往会创建出不太完整的RESTful API,因为某些元素可能会被直接添加到它的HTML部分。但与此同时,它允许先将部分元素迭代迁移到API中来实现,并随着时间的推移再对更多的元素进行迁移,具体实施步骤可以非常灵活地进行调整。