REST-API-设计模式和最佳实践实用指南-全-

REST API 设计模式和最佳实践实用指南(全)

原文:zh.annas-archive.org/md5/e392357ff9515c2847491e793441228d

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

本书旨在赋予你 API 设计原则和最佳实践的知识,以便你能够准备好设计高度可扩展、可重用、可适应和安全的 RESTful API。本书还介绍了 RESTful API 最不可或缺领域的一些常见和新兴模式。

RESTful 模式影响跨越多个功能的网络服务各个层次,例如 CRUD 操作、数据库、表示层、应用程序和基础设施层。在 RESTful 领域,其他突出和主导的模式包括通信、集成、编排、安全、管理、软件部署和交付。本书将帮助你熟悉最重要的模式,如客户端/服务器发现、API 网关、API 组合、断路器、企业安全、内容协商、端点重定向、幂等能力、API 外观等许多基本模式。

虽然本书主要涵盖关于 RESTful API 的中级到高级主题,但它也涵盖了服务导向架构和面向资源的 Web 服务架构的一些基础知识,以帮助你更快地理解所涵盖的内容。

本书面向读者

本书面向任何需要全面且易于理解的学习资源来帮助他们提升 RESTful API 设计和开发技能,并展示如何构建高度可采用的 RESTful API,这些 API 能够提供最佳实践和关键原则的见解,以及经过验证的 RESTful API 设计模式。

本书涵盖内容

第一章,RESTful 架构基础介绍,旨在更新你对网络、其架构以及其演变方式的一些基本概念的理解,希望为 RESTful 服务设计和应用打下坚实的基础。我们将讨论万维网层和架构、Web API 开发模型以及基于 REST 的服务通信。你还将了解到面向服务和面向资源的架构原则和特性,然后进一步探讨 REST 原则、约束、限定符和目标的基础。

第二章,设计策略、指南和最佳实践,讨论了一些基本的 API 设计指南,如一致性、标准化、可重用性和通过 REST 接口的可访问性,旨在为 API 设计者提供更好的思维过程进行 API 建模。此外,本章还旨在介绍一些更好的 REST API 实现实践,以及一些常见但可避免的 API 策略错误。

第三章,《RESTful API 模式精华》,不仅提供了关于概念的信息,还提供了与 RESTful API 的常见和基本设计模式相关的实际代码示例,以便您更好地理解和增强您的 RESTful API 服务。作为本章的一部分,您将学习一些常见和基本 API 设计模式,例如内容协商、URI 模板、分页、Unicode 等,以及这些模式的代码实现。每个模式都针对 RESTful 约束,并帮助您确保这些基本模式在您的 API 设计和实现中得到考虑。

第四章,《高级 RESTful API 模式》,是我们对 API 设计模式的第二次审视,旨在讨论一些高级设计模式,如版本控制、前后端分离、授权、幂等及其重要性,以及如何通过批量操作 API 来增强 API 并取悦客户。

第五章,《微服务 API 网关》,主要讨论 API 网关解决方案在使微服务对生产企业级、任务关键型、云托管、事件驱动、生产级和业务中心型应用至关重要的贡献。本章讨论了流行的 API 网关解决方案,并探讨了通过 API 网关实现聚合服务的实现。

第六章,《RESTful 服务 API 测试与安全》,旨在带您踏上 API 测试之旅,探讨 API 测试的类型、API 测试的挑战以及 API 测试中的安全问题。您将一瞥各种 API 测试工具、API 安全工具和框架,并学习如何在 API 测试、质量和安全措施中暴露任何安全问题和 API 漏洞。

第七章,《智能应用 RESTful 服务组合》,专门为您讲述 RESTful 服务范式在设计和开发下一代以微服务为中心的企业级应用以及部署中的应用。它探讨了能够相互发现和绑定在一起的 RESTful 服务如何导致过程感知、业务关键和以人为本的复合服务。您将了解服务组合的需求,各种组合方法,如编排和协奏,以及混合版本的编排和协奏在智能应用中的使用。

第八章,RESTful API 设计技巧,讨论了构建能够轻松跟上技术和业务变化的强大且兼容的 RESTful API 所需的设计模式和最佳实践。本章探讨了 API 的重要性;API 设计模式和最佳实践;API 安全指南;API 设计、开发、集成、安全和管理的各种工具和相关平台;以及 API 驱动的数字世界趋势。

第九章,对 RESTful 服务范式的更深入观察,专注于传达产生 RESTful 服务及其相应 API 的新兴技术和技巧。我们讨论了软件定义和驱动世界的方法,以及有助于快速轻松部署 API 的新兴应用类型。此外,本章还讨论了应用现代化和集成中的 REST 范式、用于数字化转型和智能的 RESTful 服务以及基于 REST 的微服务的最佳实践。

第十章,框架、标准语言和工具包,向您介绍了一些在决定 API 开发需求时可能很有用的突出框架。它讨论了几种对应用开发者来说,使用熟悉的编程语言启动他们的 RESTful API 和微服务的突出框架。本章试图为您提供有关一些编程语言友好型框架的信息,以便您可以选择最适合您 RESTful API 开发需求的框架。此外,本章还包含了一个各种框架及其支持的语言的参考表,以及它们的突出特性。

第十一章,从遗留系统现代化到以微服务为中心的应用,讨论了微服务架构MSA)是现代应用前进的方向,这些应用具有高度敏捷、灵活和弹性。本章提供了遗留应用程序现代化的原因,阐述了为什么应用程序必须现代化才能迁移并在云环境中运行,讨论了微服务和容器的组合是实现遗留系统现代化的最佳方式,并详细介绍了遗留系统现代化的方法。

为了充分利用这本书

由于本书介绍了许多网络服务和 RESTful 服务概念,您不需要遵循任何特定要求;然而,如果您想运行和执行书中提供的代码示例(您应该这样做),那么您需要对 Java 编程语言、Maven 或任何构建工具有所了解。

包含示例代码的章节对如何运行和测试示例有清晰的说明,并附带构建和运行脚本。

下载示例代码文件

您可以从www.packtpub.com上的账户下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. www.packtpub.com上登录或注册。

  2. 选择 SUPPORT 标签页。

  3. 点击代码下载与勘误。

  4. 在搜索框中输入书籍名称,并遵循屏幕上的说明。

下载完文件后,请确保您使用以下软件的最新版本解压缩或提取文件夹:

  • Windows 上的 WinRAR/7-Zip

  • Mac 上的 Zipeg/iZip/UnRarX

  • Linux 上的 7-Zip/PeaZip

本书代码包也托管在 GitHub 上,地址为github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices。如果代码有更新,它将在现有的 GitHub 仓库中更新。

我们还有其他来自我们丰富的书籍和视频目录的代码包可供在 github.com/PacktPublishing/ 处获取。查看它们吧!

使用的约定

本书使用了多种文本约定。

CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“四个基本的 HTTP 操作:GETPOSTPUTDELETE。”

代码块设置如下:

@GetMapping({"/v1/investors","/v1.1/investors","/v2/investors"})
  public List<Investor> fetchAllInvestors()
    {
       return investorService.fetchAllInvestors();
    }

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

public interface DeleteServiceFacade {
 boolean deleteAStock(String investorId, String stockTobeDeletedSymbol);
    boolean deleteStocksInBulk(String investorId, List<String> stocksSymbolsList);
}

任何命令行输入或输出都按以下方式编写:

$ mkdir css
$ cd css

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“Pipeline 实体完全负责协调控制和数据流”

警告或重要注意事项如下所示。

小贴士和技巧如下所示。

联系我们

我们读者的反馈总是受欢迎的。

一般反馈:请发送电子邮件至 feedback@packtpub.com,并在邮件主题中提及书籍标题。如果您对本书的任何方面有疑问,请通过 questions@packtpub.com 发送电子邮件给我们。

勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将非常感激您向我们报告。请访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版:如果您在互联网上发现任何形式的我们作品的非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过发送链接至 copyright@packtpub.com 与我们联系。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问 authors.packtpub.com

评价

请留下您的评价。一旦您阅读并使用了这本书,为何不在购买它的网站上留下评价呢?潜在读者可以查看并使用您的客观意见来做出购买决定,我们 Packt 可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!

想了解更多关于 Packt 的信息,请访问 packtpub.com

第一章:RESTful 架构基础介绍

Web 服务是一套计算设备向另一套计算设备提供的软件服务或软件功能。这些设备通过万维网(WWW)使用既定或标准化的通信协议进行通信。

本章旨在刷新你对 Web 及其架构的一些基本概念的理解,以及其演变的方式,希望为 RESTful 服务设计和应用打下坚实的基础。本章涵盖了以下主题:

  • 万维网(WWW)的简要历史及其演变

  • 万维网层和架构

  • Web API 开发模型和基于 REST 的服务通信

  • 服务导向架构的简要介绍

  • 资源导向架构原则和特性

  • REST 介绍

  • REST 约束

  • RESTful 限定符

  • REST 架构目标

技术要求

由于本书涉及 RESTful 设计模式的中级到高级主题,我们期望你对 Web 服务概念及其独特功能有很好的理解。如前所述,本章试图刷新你对万维网、其演变以及它提供的各种 Web 服务类型的理解,因此本章没有正式的技术要求。

网络技术演变

通常,本书的目的是提供更详细的 RESTful 模式;然而,本节旨在快速介绍自 20 世纪 90 年代初以来的 Web 服务及其演变,提供关于 Web 1.0 到 Web 3.0 的激动人心的事实,然后转向关于服务导向架构SOA)和资源导向架构ROA)的详细信息。

如你所知,今天的网络是一个宇宙本身,拥有大量的相互链接的基于 Web 的应用程序、图像、视频、照片和各种交互式内容。哪些 Web 技术使这一切成为可能,它从哪里开始,它是如何随着时间的推移而演变的,以及它是如何使 Web 应用程序开发者能够开发出令人惊叹的交互式 Web 体验的?

以下图表提供了万维网及其随时间演变的简要概述。请注意,每个 Web 版本在其对应的框中提到了其启用技术:

让我们更深入地讨论Web 3.0,并关注作为第三代的一部分的 Web 服务及其演变。

了解 Web 3.0

以下部分将重点介绍 Web 3.0 以及 Web 服务的演变和历史。

Web 3.0 通常被称为执行语义网,或读写执行网。Web 3.0 将搜索、社交媒体和聊天应用等依赖单一组织运作的服务去中心化。语义和 Web 服务是 Web 3.0 的主要组成部分。

以下图描绘了典型 Web 3.0 结构的层级。语义网层包括静态 Web翻译和基于互联网的富互联网应用RIA)或富 Web

Web 3.0 的分层结构

这个数据驱动的网络会根据用户的搜索进行调整,例如,如果用户搜索架构模式,显示的广告将更相关于架构和模式;它甚至能记住您的最后搜索,并将最后搜索的查询结合起来。有趣,不是吗?

在以下图中您可以看到 Web 3.0 栈,它由各种构建块组成,如 URI、Unicode 表示、语法(XML/JSON)、RDFS 分类法等;它们构成了 Web 3.0 栈:

Web 3.0 栈(参考:www.w3.org/DesignIssues/w3.org 

让我们继续讨论网络服务架构、规范和通信协议,因为它们是我们转向 ROA、SOA 以及表示状态转移REST)或 RESTful 服务之前的基础。

了解网络服务架构

网络服务是在网络中两个计算设备之间进行通信的方法,通信以标准化的方式(和规范)发生,用于使用 XML/JSON、SOAP、WSDL 和 UDDI 集成异构网络应用程序。XML/JSON 是提供所包含数据元数据的格式;SOAP 用于传输数据;WSDL 用于定义可消费的服务,而 UDDI 将列出可用的服务。

网络服务架构WSA)在开发任何网络服务时要求某些特性的存在,并建议一些可选的特性。

WSA 由三个重要角色组成,如下图中所示,它们如下:

  • 服务提供者

  • 服务消费者

  • 服务代理

这在以下图中展示:

服务请求者通过UDDI找到服务提供者,并使用简单对象访问协议SOAP)与提供者联系。然后,服务提供者验证服务请求,并以 XML/JSON 作为服务响应向请求者做出响应。

讨论网络 API

到目前为止,我们已经讨论了客户端/服务器/网络服务的范式基础,以及它们如何通过标准协议进行通信;然而,我们尚未触及基于 REST 的通信,毕竟这正是本书的主题。本节将介绍网络 API 的介绍,以及网络 API 是如何成为网络服务开发模型。设备之间的通信是基于 REST 的。RESTful API 不使用/需要基于 XML 的网络服务协议,如 SOAP 或 WSDL 来支持它们的接口,而是使用简化的表示。

下面的图示展示了网络API及其作为客户端和服务器端通过高级接口相互暴露的简化表示:

图片

因此,如图所示,网络API在客户端和服务器端都是可用的。客户端接口通常以 JavaScript 或浏览器插件的形式暴露,而服务器端接口通常通过 Web 以 JSON/XML 的形式暴露。我们将遇到的一些关于网络 API 的关键术语包括端点、统一资源标识符URI)和资源。

网络 API 是针对任何网络服务器或网络浏览器的应用程序编程接口API)。因此,Web API 是通过 HTTP 协议访问任何 API(可在网络上获得)的概念或方法。存在许多 API 类别,如 SOAP、XML-RPC、JSON-RPC、REST 等。API 可以用任何编程语言开发,如 Java、.NET 等。

因此,现在你已经了解了什么是 Web API 以及 REST API 开发在 Web API 领域中的位置,让我们继续前进,看看下一节中 SOA、ROA、REST、RESTful API 及其关键组成部分的更多细节。

了解面向服务的架构

面向服务的架构(Service-oriented architecture)是一种网络服务的架构风格。它定义了一些标准,并规定了设计和发展网络服务的最佳方法。任何网络服务都是具有特定结果的重复性业务活动的逻辑表示,例如获取特定城市的天气预报,访问给定股票的股价,更新库存服务的记录,等等。SOA 是自包含的,同时也提供了将服务与其他服务结合的指南。关于 SOA 的另一个事实是,它对于使用它的服务消费者来说是一个黑盒(或抽象)。

简而言之,SOA 本质上是一组服务,这些服务相互通信,而一个服务是一个定义良好、自包含且独立于其他服务上下文和状态的操作或函数。服务是托管在应用服务器上的应用程序,并通过接口与其他应用程序交互。

SOA 不是一种技术或编程语言;它是一套原则、程序和方法,用于开发软件应用程序。

了解面向资源架构

面向资源架构(Resource-oriented architecture)是语义网的基础(请参阅本章的Web 3.0部分)。ROA 的理念是使用基本、易于理解和广泛认可的 Web 技术(HTTP、URI 和 XML)以及核心设计原则。

如我们所知,Web 服务的重点是连接信息系统,而 ROA 定义了一种结构设计或一系列指南,以支持并实现任何连接资源内的交互。任何业务实体都可以表示为资源,并且可以通过 URI 使其可访问。

例如,在一个组织的人力资源系统中,每位员工都是一个实体,而薪资、员工详情和档案是该实体的关联(描述符)。

以下是一个面向对象和资源导向概念的快速比较表,它快速概述了 ROA 是什么:

面向对象架构中的对象 ROA 中的资源
每个实体都被定义为对象 实体是服务
对象具有属性和动作 服务具有描述和契约
对象需要维护状态以进行交互 通过定义的位置或地址在网络中进行交互

资源导向设计

资源导向设计部分旨在向您介绍 ROA 设计指南、设计原则和特性,以及其属性。在介绍了 ROA 属性之后,我们将在后续章节中探讨 REST 架构。

基于 ROA 的 Web 服务描述了一个可发现的自发现实体,其建模基于其逻辑形式(与服务不同,因为它们基于技术形式)。

让我们看一下以下图中 ROA 的基本块,如资源、表示法等:

前面图中的块代表了 ROA 的典型结构,并给出了服务消费者如何消费资源的想法。

让我们简要考虑 ROA 的概念和属性,如下所示:

  • 资源提供者:资源提供者向服务消费者公开资源,以便他们可以使用 HTTP 方法调用服务。微软 Azure 和亚马逊 AWS 是资源提供者的简单例子。

  • 资源:资源是对一个可识别、可分配的实体的明确引用,并且最重要的是,可以作为资源进行引用。资源的例子可能包括服务器、设备、网页、JavaScript 或软件的最新版本、软件的最新缺陷、一个组织的目录或信息列表等。

  • 资源名称:资源名称是资源的唯一名称或标识。因此,没有两个资源可以指向相同的数据。例如,软件的最新版本是 2.0.9。

  • 资源表示:资源表示是关于资源当前状态的有用信息,以特定的格式和特定的语言进行指定。

  • 资源链接和连通性:表示(链接)另一个资源或资源本身。连通性完全关乎资源链接的可靠性和相关性。

  • 资源接口:资源接口是访问资源并处理其状态的接口。

  • 可寻址性:可寻址性是将数据集或功能作为资源暴露出来,资源的可寻址性是通过 URI 实现的。

  • 无状态:无状态是保持客户端和服务器状态的隔离和独立性。来自客户端的每个请求都应该自包含。

  • 统一接口:每个服务都需要以相同的方式使用 HTTP 接口,例如 GETPOSTPUTDELETE 等。统一接口简单来说就是使用一些通用的命名法,这些命名法在互联网上被统一解释。例如,GET 确实意味着获取(读取)某些内容。

以下表格总结了可用于实现基于 ROA 的 Web 服务的 HTTP 操作:

HTTP 操作 描述
GET 读取资源表示
PUT 创建新资源
DELETE 删除资源(可选地删除相关资源)
POST 修改资源
HEAD 资源元信息

上表显示了实现 ROA 的 HTTP 方法。

ROA 的好处

以下列出 ROA 的好处:

  • 独立于客户端合约:不受接口协议/合约制定的影响,也就是说,无需制定合约,因为整个网络都是基于 HTTP 操作。

  • 显式状态:由于资源本身代表状态,服务器不会接收到未知的应用特定有效负载;服务器不需要跟踪调用服务器的客户端,客户端也不需要知道它已经与哪个服务器交谈过。

  • 可扩展性和性能:ROA 的可扩展性体现在无合约边界、显式状态以及从服务器粘性(会话)中解放客户端等特性。关于 ROA 缓存、负载均衡、索引和搜索的响应时间性能改进在提高性能方面发挥着重要作用。

通过负载均衡器在客户端和特定服务器之间创建亲和力的过程称为 会话粘性

合同或协议本质上是一组元数据,它定义了底层软件程序许多方面。

从 REST 开始

到目前为止,我们已经探讨了 ROA 和一系列指南,例如无状态、资源、可寻址性、统一资源等。这些指南是 REST 架构的基本实现。由于本书全部关于 RESTful 模式,我们将在本节中进一步探讨 REST 架构风格。

REST 概念是由 Roy Fielding 提交的博士论文。REST 的基本原则是使用HTTP协议进行数据通信(在分布式超媒体系统之间),并且它围绕资源的概念展开,其中每个组件都被视为资源,这些资源通过HTTP方法使用通用接口进行访问:

图片

一个 ROA/REST 服务的示例实现

上述图表显示了 REST 在 ROA 架构中的位置以及它如何被不同的消费者访问。

REST 是一种架构风格,而不是一种编程语言或技术。它为分布式系统提供了使用现有网络原则和协议直接进行通信的指南,以创建 Web 服务和 API,无需 SOAP 或其他复杂协议。

REST 架构简单,提供对资源的访问,以便 REST 客户端在客户端端访问和渲染资源。在 REST 风格中,URI 或全局 ID 有助于识别每个资源。正如您所知,REST 使用多种资源表示形式来表示其类型,如 XML、JSON、文本、图像等。

REST 架构风格约束

有一些设计规则应用于确定 REST 架构风格的特性,这些规则被称为 REST 约束:

图片

REST 架构风格约束

上述图表展示了典型基于 Web/互联网的应用程序中的 REST 约束。以下是一些 REST 约束:

  • 客户端-服务器

  • 无状态

  • 可缓存

  • 统一接口

  • 分层系统

  • 需求时提供代码

从客户端-服务器开始

客户端-服务器架构或模型有助于在用户界面和数据存储之间分离关注点:

图片

客户端和服务器

让我们按照以下方式在 ROA 的背景下讨论客户端和服务器:

  • 客户端:它是请求服务的组件,向服务器发送各种类型服务的请求

  • 服务器:它是服务提供者组件,根据请求持续向客户端提供服务

客户端和服务器通常由分布式系统组成,它们通过网络进行通信。

客户端在客户端-服务器架构中

单个服务器可以服务的客户端数量没有上限。客户端和服务器是否位于不同的系统中也不是强制性的。根据系统的硬件配置和服务器提供的功能或服务类型,客户端和服务器可以位于同一系统中。客户端和服务器之间的通信是通过使用请求-响应模式交换消息来实现的。客户端基本上发送一个服务请求,服务器返回一个响应。这种通信的请求-响应模式是进程间通信的一个优秀例子。为了使这种通信高效进行,有必要有一个定义良好的通信协议,该协议规定了通信规则,例如请求消息的格式、响应消息、错误处理等。所有用于客户端-服务器通信的通信协议都在协议栈的应用层工作。为了进一步简化客户端-服务器通信的过程,服务器有时会实现一个特定的 API,客户端可以使用该 API 从服务器访问任何特定的服务。

客户端-服务器架构中的服务

在客户端-服务器架构的上下文中使用的术语“服务”指的是资源的抽象。资源可以是任何类型,基于服务器(服务)提供的资源;服务器据此命名。例如,如果服务器提供网页,它被称为Web 服务器;如果服务器提供文件,它被称为文件服务器,等等。服务器可以在特定时间点接收来自任何数量客户端的请求。但任何服务器都将有其自己的处理能力限制。通常,服务器需要优先处理传入的请求,并按优先级提供服务。服务器中存在的调度系统帮助服务器分配优先级。

客户端-服务器的好处除了关注点的分离外,还有助于以下方面:

  • 提高用户界面的可移植性

  • 通过简化服务器实现来提高可扩展性

  • 使用独立、可测试的组件进行开发

理解无状态

无状态约束有助于服务更加可扩展和可靠。在 REST 的上下文中,无状态意味着所有客户端对服务器的请求都携带所有信息作为显式(声明)的,这样服务器就能理解请求,将它们视为独立的,并且这些客户端请求使服务器独立于任何存储的上下文。将会话状态保持在客户端内对于管理这种约束在服务中非常重要。

下图显示了 服务消费者(客户端)和服务状态是独立的,并且分别在客户端和服务器内部进行管理:

图片

无状态(独立管理状态)

无状态约束对服务与消费者之间允许的通信类型施加了重大限制,以实现其设计目标。以下是无状态实现的限制:

  • 客户端完全负责在客户端存储和处理所有应用程序状态及其相关信息。

  • 客户端负责在需要时向服务器发送任何状态信息。

  • 对于调用请求(客户端)的服务器上没有会话粘性或会话亲和力。

  • 服务器还需要包含客户端可能需要创建状态的任何必要信息。

  • HTTP 交互涉及两种状态,应用程序状态和资源状态,无状态适用于两者。让我们看看无状态约束在每个状态中是如何处理的:

    • 应用程序状态:存储在服务器端的数据,有助于通过当前上下文信息识别传入的客户端请求,使用之前交互的详细信息。

    • 资源状态:这被称为资源表示,它与客户端无关(客户端不需要知道此状态,除非需要作为响应),这是服务器在任何给定时间点的当前状态。

REST 的无状态约束适用于应用程序状态,即仅在应用程序状态上自由,与资源状态无关。Twitter 的 API 是无状态服务的最佳示例(GET: https://api.twitter.com/1.1/direct_messages.json?since_id=xxx&count=x)。

无状态的优缺点

以下是无状态的某些优点:

  • 由于服务器不需要管理任何会话,因此可以将服务部署到任意数量的服务器上,因此可扩展性永远不会成为问题。

  • 无状态等于更少的复杂性;无需在服务器端处理会话(状态)同步逻辑。

  • 由于底层应用程序可以缓存服务调用(请求),无状态约束降低了服务器的响应时间,即提高了响应时间方面的性能。

  • 与 HTTP 协议的无缝集成/实现是可能的,因为 HTTP 本身就是一个无状态协议。

  • 提高了可见性,因为每个请求都是其自身的资源,可以被视为一个独立请求。

  • 提高了可靠性,因为它可以从部分故障中恢复。

以下是无状态的某些缺点:

  • 增加了每次交互的开销。

  • Web 服务的每个请求都需要获取额外的信息,以便进行解析(解释),以便服务器能够从传入的请求中理解客户端状态,并在需要时处理客户端/服务器会话。

REST 中的缓存约束

缓存是指存储频繁访问的数据(在此上下文中为响应)以服务客户端请求的能力,并且不需要在需要之前生成相同的响应超过一次。良好的缓存管理可以消除部分或全部的客户端-服务器交互,并仍然以预期的响应服务客户端。显然,缓存带来了可扩展性和性能优势,包括更快的响应时间和减少服务器负载。

如您在下一张图中可以看到,服务消费者(客户端)从缓存接收响应,而不是从服务器本身接收,还有一些其他响应直接来自服务器。因此,缓存有助于减少或完全消除服务消费者与服务器之间的某些交互,从而有助于提高效率和性能(减少响应的延迟时间):

可用的缓存策略或机制有多种,例如浏览器缓存、代理缓存和网关缓存(反向代理),我们可以通过多种方式来控制缓存行为,例如通过 pragma、过期标签等。以下表格展示了可以用来微调缓存行为的各种缓存控制头:

头信息 描述 示例
Expires 表示响应被认为是过时的日期/时间头属性 Expires: Fri, 12 Jan 2018 18:00:09 GMT
Cache-control 定义了各种指令的头信息(适用于请求和响应),这些指令被缓存机制遵循 Max age=4500, cache-extension
E-Tag 服务器资源状态的唯一标识符 ETag:uqv2309u324klm
Last-modified 响应头帮助识别响应生成的时间 Last-modified: Fri, 12 Jan 2018 18:00:09 GMT

更多关于缓存控制指令的信息,请参阅 tools.ietf.org/html/rfc2616#section-14.9

缓存的好处

显然,缓存频繁访问的数据有很多好处,以下是一些显著的好处:

  • 减少带宽

  • 减少延迟(更快的响应时间)

  • 减轻服务器负载

  • 隐藏网络故障,并以响应服务客户端

缓存约束建立在客户端-服务器和无状态约束的基础上,要求响应被隐式或显式地标记为可缓存或不可缓存。

理解统一接口

如我们之前在 ROA 的统一接口部分提到的,基于 REST 的服务可以使用 HTTP 接口,如 GETPOSTPUTDELETE 等,以保持整个网络的统一性。统一接口的目的是在互联网上保留一些共同词汇。例如,GET 的确意味着从服务器获取(读取)某些内容。服务可以独立发展,随着其接口的简化和架构的解耦,统一接口也为这些资源带来了统一的词汇。以下图表展示了 HTTP 方法资源名称 对于 统一接口 的组合:

图片

Fielding 提出的四个指导原则构成了满足统一接口的必要约束,具体如下:

  • 资源识别

  • 资源操作

  • 自描述消息

  • 超媒体作为应用状态引擎

我们将在以下章节中详细讨论每个约束。

资源识别

如我们之前在早期章节中看到的,资源代表网络应用程序中的一个命名实体,它通常是 统一资源定位符URL)。因此,一个实体可以通过对其的显式引用来识别并分配为资源。

网络应用程序中的 URL 通常是一个链接,实际上它是一个 URI。例如,主页 URI,developer.twitter.com,唯一标识了特定网站根资源的概念。在 REST 约束中,我们使用的 URI 描述如下:

第二种方法提供了与第一种方法(状态/转发)类似的数据,并可能产生相同的结果或组合,但两种方法确实代表了不同的资源。

  • URI 带来了好处,如只有一个访问资源的方式,动态媒体类型用于资源响应(在请求时提供媒体类型)借助 Accept 头,以及访问这些动态资源的客户端在响应内容类型发生变化时不需要更改任何标识符。

资源操作

一旦识别资源,服务器就可以以不同的格式返回资源,例如 JSON、XML、HTML、PNG、SVG 等。这些格式是已识别资源的表示,客户端将从头部了解可能的定义良好的格式或媒体类型列表(也称为多用途互联网邮件扩展MIME))。

资源表示被客户端操纵或处理。应用程序需要支持同一资源的多种表示和相同的 URI;换句话说,相同的资源由不同的客户端以不同的方式表示。

让我们举一个例子;一个文档可能被表示为 JSON 给自动化程序,但作为 HTML 给网页浏览器。这些表示的目的在于提供一种与资源交互的方式,因此客户端可以表明他们希望接收的预期表示。

前面的概念区分允许资源以不同的方式表示,而不会改变其标识符。这是通过客户端在每次请求中通过 HTTP 头(Accept)传递给服务器的。资源通过 RESTful 应用程序发送表示来更新或添加。以下图是一个示例表示格式,它捕捉了我 Postman 工具的一个示例请求:

因此,资源表示与 URI 的解耦是 REST 的关键方面之一。

以下列表显示了可以在请求或响应中使用(作为标题)的各种内容类型表示格式:

  • Text/HTML, text/CSS, text/JavaScript

  • Application/XML, application/JSON, application/x-www-form-urlencoded

  • 图像(SVG、JPG、PNG 等)

Postman是一个帮助我们与 REST API 交互的工具。它提供了一个非常友好的用户界面,可以通过构建请求和读取响应来快速测试 API。第六章,本书的RESTful 服务 API 测试和安全提供了更多关于 Postman 工具及其更广泛的测试 RESTful API 能力的信息。

自描述消息

客户端的请求和服务器端的响应都是消息;这些消息应该是无状态的且自描述的。它们可以包含正文和元数据。RESTful 应用操作于受约束的消息类型(GETHEADOPTIONSPUTPOSTDELETE)的概念,并且被服务器和客户端完全理解。

资源期望的状态可以表示在客户端的请求消息中。资源当前的状态可能体现在从服务器返回的响应消息中。例如,一个维基页面编辑器客户端可能使用请求消息来传输表示,该表示建议更新服务器管理的网页(资源)的新状态。服务器决定接受或拒绝客户端的请求取决于服务器。

自描述消息可能包括元数据,以传达有关资源状态、表示格式和大小以及甚至消息本身的额外细节。HTTP 消息提供了头部,用于将各种类型的元数据组织成统一的字段。以下图表展示了一个示例请求及其头部,以及针对同一请求的服务器响应及其头部:

因此,在 REST 风格下的自描述消息就是关于客户端和服务器之间不维护状态,并且需要携带足够关于自身的信息或用显式状态进行解释。所以,在以下表格中,你可以看到带有示例的自描述消息:

资源 GET PUT POST DELETE
booktitles.com/resources 获取属于该集合的所有资源 替换为另一个集合 创建该集合 删除整个集合
booktitles.com/resources/title18 查找第 18 个标题 修改第 18 个标题 创建新的资源作为第 18 个标题 删除第 18 个标题

超媒体作为应用状态引擎

超媒体作为应用状态引擎HATEOAS)是其中最重要的约束之一;如果不解决它,服务就不能被称为 RESTful 服务。然而,在我们深入探讨 HATEOAS 的细节之前,让我们先简要了解一下 理查德森成熟度模型RMM),因为它是一个重要的参考,并作为任何 RESTful 服务遵循 HATEOAS 约束的指南。

RMM 是由伦纳德·理查德森开发的一个模型,它将 REST 方法的主要元素分解为 资源HTTP 动词超媒体控制。以下图表展示了 RMM 的四个级别,这些级别被用来评估 API;也就是说,API 越遵守这些约束,得分就越高:

因此,只有当 API 得分达到 Level-3 时,它才完全有资格成为 RESTful API。我们将在本章的后面部分看到更多关于如何使 API 成为 RESTful API 的指南。然而,现在你知道为什么我们在转向 HATEOAS 之前在这里提到了 RMM。

一旦客户端从服务器收到其资源请求的初始响应,它应该能够通过从同一响应中获取超链接来移动到下一个应用状态。

让我们通过一个例子来解释前面的陈述。假设一个客户端将新TODO项的表示POST到服务器,然后任务管理器应用程序的状态将通过增加TODO项列表而改变,而POSTGET都是通过超媒体链接完成的。

通过向任何已识别的资源发送自描述消息来共享资源表示。然后,它们改变应用程序的状态,并且具有接收到的超媒体链接的客户端将移动到下一个应用状态。

在 HTML 浏览器中,GET方法是通过点击具有 HREF 属性的锚标签(<a>)来完成的,HREF 包含资源 URI。POST方法是通过在具有 action URI 属性的<form>标签内按下提交按钮来实现的。锚(<a>)和表单(<form>)标签元素作为客户端请求的资源表示的一部分发送给客户端。

网络合约(共享表示)是关于媒体类型的;调用服务的客户端将知道媒体类型以及如何处理内容。因此,应用程序使服务器能够通过超媒体通知客户端改变其应用状态的可能方式。

一些媒体类型与网络很好地(和谐地)工作,它们被称为超媒体格式。包含 URI 和链接的格式是超媒体格式。

简单的 XML 并不适合超媒体,因为它不携带链接和协议。

下面的图表展示了服务器在没有和带有 HATEOAS(带有链接和 HREFs)的情况下返回的样本 JSON 响应:

图片

在我们总结这一节之前,让我们回顾一下 HATEOAS:

  • HATEOAS 意味着一个包含指向相关资源链接的应用状态表示(资源)。页面上链接的存在或缺失是资源当前状态的一个基本部分,因此对于 RESTful API 来说是至关重要的。

  • URI 是 REST 架构风格的区分器,定义 URI 非常重要,因为它将存在很长时间。因此,考虑到它们的未来,评估链接(当它们改变时)是至关重要的,或者简单地说,URI 应该在其表示经历许多变化后保持不变。关于这一点有一个有趣的阅读材料在www.w3.org/Provider/Style/URI.html.en;它详细支持了这个观点,我们鼓励您查看。

分层系统

通常,分层系统由具有不同功能单元的层组成。分层系统的基本特征是层通过预定义的接口进行通信,并且只与上层或下层通信,上层的层依赖于下层的层来执行其功能。随着架构的发展,层可以添加、删除、修改或重新排序。考虑以下层的图示:

因此,让我们从一个例子开始。REST 风格允许服务利用分层系统架构,其中我们在服务器 A 上部署 REST API,在服务器 B 上存储数据,并通过服务器 C 进行身份验证。调用 REST API 的客户端对所使用的服务器没有任何了解:

REST 架构风格建议服务可以由多个架构层组成。这些层将具有已发布的服务合同或中间件。给定层中的逻辑不能超出解决方案层次结构中直接上层或下层的层。

中间件是存在于客户端和服务器之间的层,可以添加或删除,更重要的是,不会改变组件之间的接口。

中间件具有以下特性:

  • 中间件可以是事件驱动的中间件组件,在消费者和服务之间建立处理层

  • 它们也可以是代理(由客户端选择,以提供数据转换服务、增强性能或安全保护)

  • 它们也可以是网关(由服务器或网络选择,用于数据转换、安全实施和性能提升)

客户端可能无法判断它是直接连接到服务器的端点,还是连接到实际服务器之前的中间件。中间件服务器通过拥有负载均衡器和共享缓存来帮助实现系统可扩展性的提升。层还可以为其调用客户端实施安全策略。

了解分层系统(设计)的一些应用将对我们有所帮助,因此让我们看看以下要点:

  • 允许服务客户端调用服务;被客户端调用的服务不透露任何关于它用于处理客户端请求的其他服务的任何信息。换句话说,服务消费者(客户端)只知道它直接调用的服务,不知道被调用服务所消费的其他服务。

  • 客户端和服务器之间的消息由中间件处理,帮助客户端从运行时消息处理逻辑中解放出来,并使其不知道这些消息在其他层是如何处理的。

  • 在分层系统中,在不改变服务消费者的情况下添加或删除层对于稳定性和可扩展性至关重要。

  • 请求和响应消息不会向接收者透露任何关于消息来自哪一层的细节。

尽管分层系统作为缺点带来了额外的延迟和开销,但权衡利弊,层和分层系统设计的好处如下:

  • 封装遗留服务

  • 引入中间件

  • 限制系统复杂性

  • 提高可扩展性

按需代码

在分布式计算中,按需代码COD)是指任何能够使服务器根据客户端软件的请求,将软件代码发送到客户端并在客户端计算机上执行的技术。网络中 COD 范例的知名例子包括 Java 小程序、Adobe ActionScript 语言(用于 Flash 播放器)和 JavaScript。

以下也可以称为 COD 的优点:

  • COD 是 REST 的可选约束,旨在允许在客户端网络浏览器、小程序、JavaScript 和 ActionScript(Flash)中实现业务逻辑。我认为按需视频网站是 COD 的好例子,因为视频数据文件是根据客户端系统的规格下载和播放的。

  • 根据 REST 架构风格,只有一个可选约束,即 COD。COD 允许客户端具有灵活性,因为服务器决定在客户端如何处理特定项目。例如,使用 COD,客户端可以下载动作脚本,如 JavaScript、小程序(如今不太常用)、Flex 脚本以加密客户端-服务器通信,这样底层服务器就不会意识到在过程中使用了任何特定的加密方法。

  • COD 也可以应用于服务和服务消费者。例如,服务设计可以使服务器动态地将一些逻辑部分推迟到服务客户端程序。当服务逻辑可以由消费者更有效地或更有效地执行时,将代码执行延迟到客户端的做法是合理的。

  • RESTful 应用程序可能非常能够利用支持 COD 的客户端。例如,网络浏览器可以允许服务器返回可以在客户端执行的脚本或链接。这种额外的代码执行有助于扩展客户端的功能,而无需用户安装新的客户端软件。

  • 在按需代码风格中,客户端组件可以访问一组资源,但不知道如何处理这些资源。它向远程服务器发送请求以获取表示这些知识的代码,接收该代码并在本地执行它。

然而,使用 COD 的缺点是降低了底层 API 的可见性,并不是每个 API 都喜欢这种灵活性。

COD 被视为可选的;不使用此功能的架构仍可被视为 RESTful。

RESTful 服务规范

在一个在线讨论论坛中,罗伊·菲尔德记录了他对一种声称自己是 RESTful 的服务的不满,但这种服务仅仅是基于 HTTP 的接口。该服务没有满足所有必要的 REST 架构约束。他甚至说,如果应用状态引擎(以及因此 API)不是由超文本驱动的,那么它不能是 RESTful 的,不能是 REST API.

话虽如此,任何需要被称为 RESTful 的服务都必须严格遵循强制性的 REST 架构约束。这些约束是应用于建立 REST 架构风格独特特性的设计规则。

REST 风格的创始人罗伊,强制执行以下 REST 约束,作为任何要被认定为 RESTful 的 Web 服务的强制性要求。这些强制性约束如下:

  • 客户端-服务器

  • 无状态

  • 缓存

  • 接口/统一契约

  • 分层系统

  • 可选的 REST 约束是 COD(不使用此功能的架构仍可被认为是 RESTful)

每个约束都是一个预定的设计决策,并将对服务产生积极和消极的影响。然而,这些约束旨在提供更好的架构,使其类似于网络(可能是积极影响与消极后果的平衡)。

在偏离 REST 约束时,可能需要做出潜在的权衡。确保这些权衡不会削弱或消除规定的约束。如果是这样,该架构可能就不再符合 REST,换句话说,服务(架构)就不是 RESTful 的。

REST 的架构目标

REST 架构风格带来了一组属性,有助于建立嵌入在 REST 约束应用中的设计目标。这些属性如下:

  • 性能

  • 可扩展性

  • 简单性

  • 可修改性

  • 可见性

  • 可移植性

  • 可靠性

  • 可测试性

前述属性标志着软件架构的目标状态和万维网的基本品质。在设计决策中遵循 REST 约束有助于实现上述列出的目标,当然,这些属性可以通过更多不是 REST 必要部分的设计决策进一步改进。然而,正如在RESTful 服务要求部分引用的那样,一个被称为 RESTful 服务的 Web 服务应该遵守 RESTful 约束。

摘要

让我们总结一下本章所涵盖的内容,以及我们在 REST 架构风格背景下可以吸取的教训。我们从一个关于万维网演变、其层和其架构的简要历史开始。然后我们转向 Web API,它是 Web 服务的一个开发模型,以及基于 REST 的服务如何通过简化的表示与现有的 Web 协议进行通信。

我们简要地探讨了 SOA,并更详细地研究了 ROA。我们涵盖了 ROA 的目的(使用简化、易于理解和广泛认可的 Web 技术以及核心设计原则),其原则以及其特性。在为 ROA 打下基础之后,我们了解了 REST 的概念;REST 架构的强制性约束,如客户端-服务器、无状态、可缓存、统一接口和分层系统;以及可选的按需代码约束。

作为统一接口的一部分,我们学习了其四个指导原则,即资源的识别、资源的操作、自描述消息和 HATEOAS,我们还简要提到了 RMM 的重要性,以获得构建 RESTful 服务的基础。

本章详细描述了五个强制性约束以及 Roy 如何看待它们定义了 RESTful 的架构风格。理解什么是 RESTful 以及什么不是 RESTful 至关重要。

我们通过快速介绍 WWW 的软件架构属性以及 REST 约束如何帮助实现 REST 的架构目标,如性能、可扩展性和简单性来结束这一章,这些目标包括性能、可扩展性和简单性。

希望你们喜欢这一章。在下一章中,我们将学习 API 设计策略,例如自助服务启用、资源协作,以及如何解决安全性和可扩展性问题,同时遵循 RESTful API 指南和组成部分。

第二章:设计策略、指南和最佳实践

在当今数字化世界中,挑战在于将各种异构设备与互联网和内网中丰富的软件服务相互连接。应用程序编程接口API)是解决任何任何时间任何地点任何设备的最有前途的软件范例之一,这是数字世界当前的一个基本需求。本章讨论了 API 和 API 设计如何帮助解决这些挑战并弥合差距。

本章讨论了一些基本的 API 设计指南,例如一致性、标准化、可重用性和通过 REST 接口的可访问性,这些可以帮助 API 设计者更好地思考他们的 API 模型。

以下为本章的目标:

  • 了解 REST API 及其重要性

  • RESTful API 设计的目标

  • API 设计师的角色和责任

  • API 设计原则

  • RESTful API 设计规则

此外,本章旨在介绍一些更好的 REST API 实现的特征和组成部分,以及一些常见但可避免的 API 策略错误。

技术要求

本章主要旨在向读者介绍涉及设计策略和最佳实践的多种 RESTful 设计概念;我们期望读者已经对软件设计概念、客户端-服务器架构以及基本的数据交换格式(如 JSON 和 XML)有基本的了解。

我们假设读者对网络架构、HTTP 方法、头部以及相关的客户端-服务器概念有基本的了解。然而,我们强烈建议我们的读者刷新他们对设计原则(如 SOLID、面向对象设计、企业集成、SOA 和微服务架构基础)的知识。

了解 REST API 及其重要性

在第一章中,我们学习了 RESTful 原则,如客户端-服务器、无状态、可缓存和分层,我们将在本章中识别并应用这些原则到低级但功能性的 API 中。此外,我们还将学习 RESTful API 设计策略和最佳实践,这些可以帮助我们应对任何时间任何地点任何设备的挑战。

让我们考虑一个例子——假设你将参加一个活动,其中除了其他活动外,还将放映电影 《索洛》星球大战系列中的最新作品),而你知道将参加派对的你的某些朋友对 《星球大战》 和早期续集并不熟悉。所以作为一个技术极客,你想要通过一个简单的移动应用程序帮助你朋友,该应用程序可以将有关 《星球大战》 角色详情、电影列表、星际飞船等的信息以消息的形式发送到你的 WhatsApp 群组;同时,当你的任何朋友请求任何特定信息时,它也会发送消息。

但是,一个独立的移动应用如何获取星球大战的数据,它将如何使用 WhatsApp 进行回复,我们如何将三个不同的系统(移动、消息和数据)集成,并帮助你的朋友解决他们的星球大战查询?

我们需要自己创建一个庞大的星球大战数据集吗?移动应用将如何利用 WhatsApp 的消息功能来发送星球大战信息?消息如何成为我们应用的搜索字符串?我们还不清楚我们还需要解决多少未知的问题作为开发的一部分。

你认为如果星球大战数据集可以随时可用并可搜索,那就太棒了,而且 WhatsApp 应该具备向朋友发送消息的编程能力。

经过一些研究,我们发现以下清单,这让我们有信心在短时间内开发出移动应用:

  • SWAPI (swapi.co/)上关于星球大战的可用数据集,该应用可以利用,不仅如此,它还提供了一个我们可以搜索并获取响应的机制,因此我们有了关于星球大战数据集/数据存储的解决方案

  • WhatsApp 还提供了现成的消息功能,你的程序可以使用它来发送消息

基于前面的信息,让我们将这些提示按顺序排列,以可视化该应用:

  1. 一个朋友在 WhatsApp 上询问关于 Beru 的事情,因此应用捕捉到了这个信息。

  2. 应用使用swapi.co/api/people/?search=beru

  3. SWAPI REST API 向应用请求发送响应。应用根据收到的响应创建关于 Beru 的详细信息文本。

  4. 应用构建一个点击聊天API 请求,并将消息发送到群组。

  5. https://api.whatsapp.com/send?text=<"Beru Whitesun Lars, born: '47BBY', she belongs to *Human* Species and from *Tatooine* Planet">

  6. 你的朋友/群组收到消息——Beru Whitesun Lars, born: "47BBY", she belongs to "Human" Species and from "Tatooine" Planet

因此,通过拥有所有必要的 REST API 详细信息,你将获得信心来构建一个移动应用,帮助你将搜索结果作为消息发送到 WhatsApp 群组,你准备好摇滚派对了。有趣,不是吗?

因此,现在你知道 SWAPI 和点击聊天(api.whatsapp.com)都是 REST API,可以被任何应用消费,就像你的移动应用消费那些 API 一样。在我们的例子中,我们是否可以说该应用是尝试通过 RESTful API 解决随时随地任何设备的数字化挑战?

Google Maps 和位置、Apple iTunes、Google Books、英国警方(data.police.uk/api/forces)、日出和日落时间(sunrise-sunset.org/api)以及英国国家书目(bnb.data.bl.uk)是公共 API 的几个例子。

我们迄今为止看到的 REST API 示例主要是读取操作。然而,在现实中,API 可以做得更多,我们将展示如何设计 RESTful API,它能够支持创建、读取、更新和删除(CRUD)操作、分页、过滤、排序、搜索等等,正如您阅读这本书时所见。

我们鼓励您了解各种公开可用的 API,包括它们的目的、响应格式等内容,这将有助于您理解并跟进本章的后续讨论。

RESTful API 设计的目标

从我们迄今为止看到的 API 示例中,您可能已经注意到它们简单直接、明确、易于消费、结构良好,最重要的是,可以通过众所周知的标准化 HTTP 方法访问。

到目前为止,我们对 API 有了相当的了解,并且它们是解决许多数字化挑战的最佳解决方案之一;通过我们之前的示例,我们也知道谁在使用这些 API。现在让我们思考如何创建这样的可用 API 并将其公开供消费。在深入 API 设计之前,我们是否必须考虑一些基本和必要的原则?API 应该允许消费者做什么?消费者想用它做什么?为了回答我们的问题,我们需要了解以下 API 设计目标:

  • 便利性

  • 松散耦合

  • 利用现有的网络架构

让我们更详细地讨论一下。

便利性

让我们讨论一个基本的设计概念,称为便利性,因为它可以回答我们之前提出的各种问题。便利性意味着对象及其属性是如何被其设计所感知的。在这里,它提供了关于其操作的线索:

图片

上述图表帮助我们理解我们所谈论的设计世界中的“便利性”是什么;在我们看到的开关中,第一个代表旋转操作,另一个表示上下操作。因此,仅通过观察这些物体,我们就能感知它们支持什么以及如何支持。在 API 设计的情况下,便利性无疑起着至关重要的作用,并且是我们 API 设计的一个基本方面。

松散耦合

由于 API 的整个目的是将异构客户端与相同的后端代码连接起来,因此 API 尽可能地独立和尽可能地与调用客户端松散耦合是不可避免的。

在松散耦合的设计中,API 是独立的,一个 API 的修改不会影响消费者的操作。在 API 内部,组件会被添加、修改或替换。然而,松散耦合的方法在组件被添加、替换或更改时,为客户端提供了更好的 API 灵活性和可重用性。

在 REST API 服务器设计中采用松耦合架构,使得客户端和服务器都遵循并尊重共同的语义。如果 API 修改了其响应的含义,那么客户端需要意识到这一点,并相应地采取行动。

优秀的 API 展现出跨服务边界松耦合和良好组成的函数,以最大化可扩展性因素。

利用网络架构

自 1989 年由蒂姆·伯纳斯-李爵士发明以来,网络的基本原理仍然是所有网络架构的基础,即使今天也是如此。正如你们所知,HTTP 是网络架构的生命线,它为所有客户端请求、服务器响应以及整个网络上的文档/内容的传输提供动力。因此,确保 REST API 通过构建任何设备或操作系统都可以消费的接口来拥抱其强大的爆发力是至关重要的。

RESTful API 应使用 HTTP 作为传输层,因为 HTTP 的基础设施、服务器和客户端库已经广泛可用。

RESTful API 应利用 HTTP 方法,或动词,如由 RFC 2616 协议定义的GETPUTPOST

RFC 2616(tools.ietf.org/html/rfc2616)定义了 HTTP(分布式、协作和超媒体信息系统应用层协议)的互联网标准。

API 设计者角色和责任

在我们讨论 API 设计目标、原则和实践之前,让我们简要谈谈软件架构师、解决方案架构师、软件设计师或任何准备承担设计 RESTful API 责任的角色的主要职责。

要生产出成功的 API,API 设计者应该具备或做以下事情:

  • 精通 REST 基础和 API 设计最佳实践

  • 熟悉 API 设计模式,以创建现代 API 设计

  • 关注本章讨论的因素,例如 API 设计目标和最佳实践,这些可以改善应用程序开发者的体验

  • 通过对业务愿景及其功能的清晰理解,将业务领域转换为几个 API

  • 与 API 开发者紧密合作,帮助他们处理日常限制,并处理他们现有的遗留架构

  • 设置涉及开发者反馈会议、原型、测试用户、发布和版本控制的反馈循环

  • 使用反馈循环将经验融入他们的 API 设计中,并加快 API 开发速度

  • 创建一流的文档、可重用代码库、示例代码和教程

既然我们了解了 API 设计者的角色和责任,那么让我们继续讨论如何通过一些行业 API 最佳实践来设计成功的 API。

API 设计最佳实践

让我们思考一下任何网络服务 API 的消费者是谁。会是另一个系统、另一个软件应用,还是最终用户?大多数情况下,API 的消费者是另一个软件应用或另一个系统本身。因此,我们可以得出结论,任何 API 的客户将是那些赋予软件生命、使其通过编程代码变得有目的性和可用性的应用开发者。因此,API 在很大程度上依赖于应用开发者或应用开发者。

因此,应用开发者应该是 API 设计的首要关注点,并且为了让他们能够使用 API,应该定义并使业务功能可访问。请务必记住,如果没有任何应用开发者或应用开发者准备好使用 API,该 API 将不复存在。

以下是由 API 设计者使用的最佳实践列表,以产生应用开发者喜欢使用的 API:

  • 保持 API 简单易用——简化、友好和直观的 API 总是吸引应用开发者(我们 API 的客户端),最大限度地发挥应用开发者的潜力,使应用开发者的生活更加舒适、痛苦更少、更高效。

  • 提供定义良好且易于识别的业务功能。

  • 使 API 可通过任何标准网络浏览器访问——通过现有的网络基础设施(HTTPs GETPOSTPUTDELETE)暴露 API,并通过标准浏览器访问,使得底层 API 平台无关。

  • 抽象服务内部和领域模型——最好的 API 只暴露 URI 和有效负载,而不暴露服务内部或领域模型。一个例子是www.googleapis.com/books/v1/volumes

  • 确保 RESTful API 有效负载不包含任何 SOAP 有效负载的痕迹,因为客户端不同(机器与人类)。

  • 保持一致性——API 实现应无变化或矛盾;通过设定明确的标准,帮助消费者了解从 API 中可以期待什么,并在 API 中实施类似的行为,如搜索和过滤(或分页和限制)。

  • 实现标准 URL 模式——标准 URL 模式的一个例子是/collection/item/collection/item,其中/collection可以是书籍、狗、事件(复数)等等。

  • 练习标准术语——在 URI 中遵循标准和有意义的元素对于 API 的成功至关重要。标准术语的一个例子是bookIddogIdeventId,而不是bIddIdeId

  • 保持灵活性——API 能够接受来自客户端的输入。一个例子是/planner/v1/tasks/planner/v1/Tasks/planner/v1/TASKS;在上面的例子中,小写、大写或驼峰式命名法应该是可接受的,并且应该以相同的方式表现。

  • 保持稳定——API 的增量修改是不可避免的,但它应该独立于客户端应用程序。换句话说,不应该对使用经过修改的 API 的客户端进行强制修改。比如说,/books/v1/volumes不涉及对客户端的任何更改,当体积模块经过一些更改时,提供额外的利益/缺陷修复。

  • 应该有一个清晰的错误处理和错误消息处理机制——API 实现不仅应该提供更好的业务功能;它处理错误和错误消息的能力至关重要,它应该提供有用且易于理解的错误消息,包括应用程序开发者可以理解的诊断信息,因为错误消息提供了提示并帮助应用程序开发者解决可能导致的错误。

  • 文档——API 是可发现和有文档记录的,因此发布 API 文档是必须的。API 文档包括入门指南、示例代码、示例请求、示例响应、示例实现、关于认证和错误处理的详细说明、关于反馈途径的信息等等。

  • 为 API 用户提供反馈和支持机制。

是否有 API 设计实践会很好,这样你就可以快速启动 API 设计?不,还不是时候。我们需要了解一些核心的 API 设计原则,我们将在下一部分回顾。

API 设计原则

为了创建灵活、可扩展和安全的 API,API 设计者需要一套指导方针。我们将讨论以下基本原则:

  • 普遍的网络标准

  • API 灵活性

  • API 标准化

  • API 优化

  • API 粒度

  • API 沙盒或游乐场

通过这样做,我们将能够理解遵循这些原则将如何帮助我们设计高质量的 RESTful API。

普遍的网络标准

正如我们在RESTful API 设计目标部分所讨论的,API 设计者应该采用现有的网络标准,并开发他们的 API 设计和平台,从而实现 RESTful API 和客户端之间的普遍通信。

让我们提出一些问题,这些问题将帮助我们为我们的 API 推导出更好的设计原则:

  • 谁将使用我们的 API?

  • API 需要支持哪些业务功能?

  • API 应该有多细粒度?

  • API 是否应该始终遵循现有的网络标准并提供一致性?

REST 架构风格坚持采用现有的网络标准,因此利用这些标准应该是任何 API 设计的首要关注点。以下图表展示了几个常见的网络方法,即GETPOSTPUTDELETE,以及客户端与REST API的交互:

图片

在网络方法之后,还有一些基本的设计方面,API 应该遵守,它们解决了关于标准化、API 消费者和 API 一致性的问题:

  • 任何应用程序客户端都应该能够在不参考大量文档的情况下理想地使用 API

  • 使用标准 HTTP 方法调用,在每种语言和平台上都可用,来向 API 发送请求和检索信息

  • 不要对消费者应用使用的软件开发技术做出任何假设

  • 网络协议 HTTP 以及 JSON 或 ATOM 等响应有助于 API 客户端找到连接到任何语言或平台的库

灵活性

API 的数据应独立于资源或方法。这意味着 REST API 应处理多种类型的调用并返回各种数据格式,即使超媒体表示的结构有所变化。换句话说,API 响应的数据不绑定到资源或方法。

GitHub API 的摘要表示和详细表示可能是 API 灵活性的示例。仓库 API GET /orgs/myorg/repos 获取摘要表示,而单个仓库 API GET /repos/myorg/myhelloworld.rb 获取指定仓库的详细描述。

Salesforce API 通过其响应格式提供灵活性,因此 API 开发者可以将数据序列化为 XML 或 JSON 格式。

为 API 开发者实现的 GraphQL 端点实现是 API 灵活性的另一个最佳示例。使用 GraphQL,开发者可以根据预定义的模式请求他们想要的数据,因此 API 可以按照预定义的模式进行响应:

上述截图反映了针对特定字段(书名和评分)的样本 GraphQL 请求(模式)和指定字段的响应。

粒度

粒度是 REST API 设计的基本原则。正如我们所理解的,将业务功能划分为许多小的动作是细粒度的,那么将业务功能划分为大操作则是粗粒度的。然而,关于 API 需要达到何种粒度级别的讨论可能会有所不同;我们可能会得到不同的建议,甚至陷入辩论。无论如何,最好基于业务功能和其用例来决定,因为粒度决策无疑会根据具体情况而有所不同。

在某些情况下,网络调用可能很昂贵,因此,为了最小化这些调用,粗粒度 API 可能是最佳选择,因为客户端的每个请求都会在服务器端强制执行大量工作,而在细粒度 API 中,需要许多调用才能在客户端完成相同的工作量。

考虑以下示例。一个服务在一次调用中返回客户订单(例如n个订单);这是粗粒度 API 的实际应用。在细粒度的情况下,它只返回客户 ID,而对于每个客户 ID,客户端需要额外发起一个请求以获取详细信息,因此客户端需要发起n+1次调用;这可能是在性能和网络响应时间上昂贵的往返调用。

在其他一些情况下,API 应该设计在最低的实际粒度级别,因为可以通过满足客户需求的方式将它们组合起来。

现在,看看这个例子,电子表单提交可能需要收集地址以及,比如说,税务信息。在这种情况下,有两个功能:一个是收集申请人的位置信息,另一个是收集税务详细信息。每个任务都需要通过不同的 API 来处理,并需要单独的服务,因为地址变更在逻辑上是一个不同的事件,与税务时间报告无关,也就是说,为什么在地址变更时需要再次提交税务信息。

粒度级别应该满足业务功能或用例的特定需求。虽然目标是最大限度地减少网络调用并提高性能,但了解 API 消费者需要的操作集以及这些操作如何有助于我们设计中的正确粒度 API 是很重要的。

例如,对于那些可以接受多次调用 API 服务器的内部服务消费者,这些 API 可以设计为细粒度,而对于外部消费者,如果他们需要避免多次往返 API,则可以设计为粗粒度。

有时,API 设计可能需要同时支持粗粒度和细粒度,以给 API 开发者提供灵活性,让他们为他们的用例选择正确的 API。

以下要点可以作为读者决定他们在 API 建模中的 API 粒度级别的一些基本指南:

  • 通常情况下,将服务视为粗粒度,将 API 视为细粒度。

  • 在响应数据量和提供这些数据所需资源数量之间保持平衡,将有助于决定粒度。

  • 在定义粒度时,还应考虑在数据上执行的操作类型。

  • 读取请求通常是粗粒度的。在某些情况下,返回所有必要的信息以渲染页面不会像两次单独的 API 调用那样造成太大的伤害。

  • 另一方面,写入请求必须是细粒度的。找出客户端需要的常见操作,并为该用例提供特定的 API。

  • 有时,你应该使用中等粒度,也就是说,既不是细粒度也不是粗粒度。以下示例中,嵌套资源位于两层深度内。

考虑以下快照,它反映了一个中等粒度的 API 响应:

图片

让我们以一个广泛的指导方针来结束本节,这个指导方针有助于确定正确的服务粒度——识别服务影响的关键业务实体,并相应地建模生命周期;也就是说,对于每一个业务结果,应该只有一个 API 操作。

前面的指南可能导致许多 API 部署单元,这可能会在后续造成不便。有一些模式,特别是 API 网关,可以更好地协调这些众多的 API。通过优化端点、请求合并等方式协调 API 有助于解决粒度挑战。

优化 API

本节讨论了为 API 采用更好的优化方法。没有一种适合所有情况的方案。在现实世界中,多个 API 可能支持相同的服务,因为该服务可能服务于不同类型的用户和用例。正如我们在本章前面引用的那样,API 应根据其满足的用例进行建模,而不是根据其暴露的后端服务或应用程序。

因此,优化适用于特定上下文中的特定业务请求。让我们以一个例子来说明,一个网络服务允许其移动应用用户支付电费。在移动应用的环境中,我们应该首先考虑移动应用的限制,因为移动应用对网络延迟、多次网络请求、数据大小与标准网络应用相比等因素都很敏感。因此,我们的 API 设计应侧重于限制后端调用并最小化返回的数据大小。

关于粒度,让我们考虑前面的例子将消耗少量细粒度的独立可调用 API。另一方面,为了进行支付,应用程序可能需要使用来自另一个服务的粗粒度 API(该服务本身可能包含许多细粒度 API)。因此,我们的移动应用可以直接使用一些其他细粒度 API 来获取应付款项、获取用户地址以及访问用户想要用来支付未付款项的银行的账户详情,甚至可能需要在未来集成更多细粒度 API 等。因此,设计者应考虑我们在第一章中讨论的分层或分层方法,以协调和管理这些细粒度 API。

因此,API 设计可以公开细粒度的 API 供消费者直接访问,并在其之上提供粗粒度的服务,以支持更广泛的使用案例,这样服务客户端可以选择直接调用细粒度 API,或者如果他们需要多个细粒度 API 的合并功能,他们可以选择使用粗粒度 API。

图片

前面的图展示了 API 的逻辑结构及其粒度,以及这些 API 是如何被服务客户端消费的,这是我们之前讨论的使用案例的优化 API 示例。

功能性

这种设计原则表明,API 设计应该支持生命周期作为一个单一窗口的完整过程。例如,在电子商务网站上,当消费者购买商品时,他们不应该需要去银行门户网站检查余额或进行支付,也许它应该集成在同一电子商务门户网站内。电子商务门户网站使用的 API 应该涵盖生命周期的全过程。部分或未完成的 API 会严重影响用户体验。

对于 APIs 来说,另一个需要考虑的方面是在当今金融世界中提供全面覆盖;随着现金管理、自动转账、证券交易所订单等服务领域的快速扩张,APIs 是连接这些第三方服务和银行服务的完美解决方案。

我们在前面章节中看到的用于识别服务粒度的相同实体生命周期建模方法,也有助于我们了解哪些服务是支持完整业务流程生命周期所必需的。

了解不寻常情况

在现实世界中,有一些只能通过专用或专有技术解决的问题。这些情况是不寻常的,那些情况的一些例子可能出现在智能家居平台、具有标准化 B2B 模型的物联网(现场总线)实施、ebMS3/AS4 消息等场景中。

在实用 REST 服务世界中,总会有一些情况,API 设计者会遇到不寻常的情况,需要为他们的 API 设计做出一些权衡,例如,将遗留应用程序迁移到 RESTful 服务场景。实际上,迁移整个遗留软件是不切实际的,特别是在专有代码的情况下,我们将这些情况称为特殊情况。然而,在那些特定情况下,有一些设计原则和模式,如领域驱动设计模式,会派上用场。这些不寻常或独特的情况也属于设计原则范畴,它们建议 API 只为绝对必要解决特定问题的案例提供专门技术,而不仅仅是因为情况复杂。

适用性声明 4AS4)是一个使用 Web 服务的开放标准协议规范,用于安全且与有效载荷无关的企业到企业B2B)文档(OASIS ebMS)。

鼓励您参考由 Packt 出版的书籍架构模式,其中有一章专门介绍领域驱动设计DDD)模式,并详细讨论了许多 DDD 模式。

社区标准化

遵循由开放联盟描述的标准和命名约定,使我们的 APIs 更加易于使用和互操作性。开放旅行联盟OTA)和开放地理空间联盟OGC)是这些联盟的例子。

社区标准化原则建议,当可用时,API 实现应使用行业标准信息组件进行设计。例如,iCalendar 用于日历邀请和事件,vCard 用于姓名和地址信息,Keyhole Markup Language (KML)用于地理空间数据,这些都是我们 API 可以充分利用的明确定义的标准。

API 游乐场

API 提供商应开发和公开一个相关的网站/开发者门户,以便开发者可以快速了解他们的 API。它为新客户提供文档、论坛和带有安全 API 访问密钥的自助服务。

APP 开发者了解 API 及其提供的内容,不仅通过文档,而且通过在可以以受控、监控的方式测试和操作数据的环境中使用的简单工具和技术,这一点至关重要,并在开发者中引发了巨大的兴趣去学习和使用 API。

一个交互式且在浏览器中的 API 游乐场是潜在用户识别 API 端点和测试其代码以体验 API 行为的最有效方式之一。

沙盒、虚拟化和 API 游乐场是 API 提供商吸引应用开发者体验 API 功能的三种不同方式。

API 沙盒是一个在特定区域内具有特定规则的控制环境,提供简单的 API 调用。虚拟化是真实 API 的镜像,为 APP 开发者提供类似生产环境的测试环境。API 游乐场比沙盒提供更多功能,但与虚拟化不同,它们是一个有限的、受控的系统模拟。API 游乐场最适合开发者测试并从 API 中获得更多数据集;同时,API 提供商对这些环境也有更好的控制。

RESTful API 设计规则

现在我们已经了解了 API 设计师的角色和职责、API 设计最佳实践和 API 设计核心原则,我们可以再覆盖一个重要的 API 设计方面,即称为API 规则。最佳实践和设计原则是 API 设计师试图在其 API 设计中融入的指南。然而,API 规则需要在 API 设计中进行调整,以使我们的 API 符合 RESTful 风格。因此,本节专门介绍以下 RESTful API 规则:

  • 统一资源标识符的使用

  • URI 权限

  • 资源建模

  • 资源原型

  • URI 路径

  • URI 查询

  • 元数据设计规则(HTTP 头和返回错误代码)以及表示

  • 客户关注的问题(版本控制、安全性和超媒体处理)

我们相信,对这些规则的清晰理解将使我们更接近设计,并开始我们的旅程,朝着提供最优质的 RESTful API 迈进。

了解统一资源标识符

REST API 应使用 统一资源标识符(URIs)来表示其资源。资源指示应清晰直接,以便它们能够清晰明确地传达 API 资源:

  • 一个简单易懂的 URI 示例是 https://xx.yy.zz/sevenwonders/tajmahal/india/agra,如您所观察到的,强调的文本清楚地表明了意图或表示

  • 一个难以理解的 URI 示例是 https://xx.yy.zz/books/36048/9780385490627;在这个示例中,“books”之后的文本对任何人来说都很难理解

因此,在 RESTful API 设计中,URI 的简单、易懂的表示至关重要。

以下部分讨论了针对 RESTful 服务的许多此类 URI 方面。

URI 格式

根据 RFC 3986,通用 URI 的语法是 scheme "://" authority "/" path [ "?" query ] [ "#" fragment ],以下是为 API 设计制定的规则:

  • 使用正斜杠(/)分隔符:这用于表示资源之间的层次关系,例如,http://xx.yy.zz/shapes/polygons/quadrilaterals/squares

  • 不要使用尾部正斜杠:URI 中的尾部正斜杠没有任何意义,可能会造成混淆,例如,http://xx.yy.zz/shapes/polygons/http://api.examples.org/shapes/polygons(注意 URI 末尾的尾部斜杠)。REST API 既不应期望尾部斜杠,也不应在提供给客户端作为响应的链接中包含它们。

  • 使用连字符(*-*:连字符可以提高 URI 名称、路径和段落的可读性,并帮助客户端轻松扫描和解释,例如,https://xx.yy.zz/seven-wonders/taj-mahal/india/agra(注意连字符分隔了“七奇迹”和泰姬陵之间的空间)。

  • 避免下划线(_:设计师应避免在路径中使用下划线(_)表示,因为字符下划线在计算机字体渲染时可能会部分遮挡或隐藏,例如,https://xx.yy.zz/seven_wonders/taj_mahal/india/agra(注意我们无法辨认下划线,因为它被作为超链接)。应使用连字符代替 https://xx.yy.zz/seven-wonders/taj-mahal/india/agra(使用连字符,即使它是超链接,也能清晰可见)。

  • 在 URI 路径中优先使用全部小写字母:API 设计师应优先考虑小写字母,而不是其他任何表示形式,因为 RFC 3986 将 URI 定义为大小写敏感,除了方案和主机组件。一些例子包括,http://xx.yy.zz/shapes/polygonsHTTP://XX.YY.ZZ/shapes/polygons 是相同的,而 http://xx.yy.zz/shapes/polygonsHTTP://XX.YY.ZZ/SHAPES/Polygons不是 相同的。

  • 不要包含文件扩展名:正如你所知,文件名后缀的点(.)表示其文件类型。然而,URI 不应该使用点来表示任何文件扩展名;相反,它应该依赖于通过内容类型头(参考媒体类型和媒体类型设计规则部分)传达的媒体类型。

REST API URI 授权

正如我们所见,对于通用 URI 有不同的规则,我们将讨论 REST API URI 的授权(scheme "://" authority "/" path [ "?" query ] [ "#" fragment ])部分:

  • 使用一致的子域名:

    • API 的一致子域名包括以下内容:

      • 顶级域名和第一个子域名表示服务所有者,一个例子可以是baseball.restfulapi.org

      • 如你所见在http://api.baseball.restfulapi.org,API 域名应该包含子域名中的api

    • 开发者门户的一致子域名包括以下内容:

      • 正如我们在API 游乐场部分所看到的,API 提供者应该为 APP 开发者提供测试 API 的网站,称为开发者门户。因此,按照惯例,开发者门户的子域名应该包含developer。一个包含开发者的开发者门户子域名的例子将是http://developer.baseball.restfulapi.org.

资源建模

资源建模是 API 设计者的主要方面之一,因为它有助于建立 API 的基本概念。

在前面的部分中,我们看到了关于 URI 的详细信息;让我们一般性地考虑,URI 路径总是传达 REST 资源,并且 URI 的每一部分都由一个正斜杠(/)分隔,以表示模型层次结构中的唯一资源。

让我们看看以下示例 URI 设计:

每个资源通过正斜杠分隔表示一个可寻址的资源,如下所示:

客户、配置文件和 API 都是前面单个 URI 模型中的独特资源。因此,资源建模是一个关键的设计方面,API 设计者在设计 URI 路径之前需要考虑 API 资源模型。

资源原型

API 提供的每个服务都是一个原型,它们表示 REST API 设计的结构和行为。资源建模应该从几个基本的资源原型开始,通常 REST API 由四个独特的原型组成,如下所示:

URI 路径

本节讨论与 REST API URI 中有意义的 URI 路径(scheme "://" authority "/" path [ "?" query ] [ "#" fragment ])设计相关的规则。

以下是一些关于 URI 路径的规则:

URI 查询

本节讨论与 REST API URI 的查询部分(scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]*)设计相关的规则。

URI 的查询部分也代表了资源的唯一标识,以下是一些关于 URI 查询的规则:

  • 使用查询对集合或存储进行过滤:

    • 查询中的限制示例:https://api.lufthansa.com/v1/operations/flightstatus/arrivals/ZRH/2018-05-21T06:30?limit=40
  • 使用查询对集合或存储结果进行分页:

    • 查询中的偏移量示例:https://api.lufthansa.com/v1/operations/flightstatus/arrivals/ZRH/2018-05-21T06:30?limit=40&offset=10

HTTP 交互

REST API 不建议使用任何特殊的传输层机制,它只需要基本的超文本传输协议及其方法来在网络上表示其资源。我们将在接下来的章节中讨论 REST 应如何利用这些基本的 HTTP 方法。

请求方法

客户端通过定义明确的语义 HTTP 方法(如 GETPOSTPUTDELETEPATCHHEAD,和 OPTIONS)来指定预期的交互。以下是在设计时 API 设计师应考虑的规则:

  • 不要使用 GETPOST 方法进行其他请求的隧道传输

  • 使用 GET 方法检索资源的表示

  • 使用 HEAD 方法检索响应头

  • 使用 PUT 方法更新和插入存储的资源

  • 使用 PUT 方法更新可变资源

  • 使用 POST 方法在集合中创建新的资源

  • 使用 POST 方法执行控制器操作

  • 使用 DELETE 方法从其父资源中删除资源

  • 使用 OPTIONS 方法检索元数据

响应状态码

HTTP 规范定义了标准状态码,REST API 可以使用相同的状态码来传递客户端请求的结果。

状态码类别和一些相关的 REST API 规则如下,以便 API 可以根据进程状态应用这些规则:

  • 1xx: 信息性: 这提供了协议级别的信息

  • 2xx: 成功: 客户端请求被接受(成功),如下所示:

    • 200: OK

      • 用于指示客户端请求成功

      • 不要用于在响应体中传达错误

    • 201: 已创建

      • 用于成功创建资源
    • 202: 已接受

      • 用于报告成功的异步操作
    • 204: 无内容

      • 当 API 想要在响应体中发送空或无内容时
  • 3xx: 重定向: 服务器将客户端请求重定向到不同的端点以满足客户端请求:

    • 301: 永久移动

      • 用于已移动的资源
    • 302: 找到

      • 请注意不要使用302,因为它会在开发者中引起关于从客户端自动重定向的混淆
    • 303: 看其他

      • 用于将客户端指向不同的 URI(代替302,建议 API 应使用303
    • 304: 未修改

      • 使用以便客户端可以节省带宽

      • 与条件 HTTP 请求一起使用

    • 307: 临时重定向

      • 用于指示客户端将请求重新提交到另一个 URI
  • 4xx: 客户端错误: 客户端错误:

    • 400: 错误请求

      • 可以用来指示通用或非特定失败
    • 401: 未授权

      • 用于客户端未经授权访问或客户端凭证问题
    • 403: 禁止

      • 用于禁止访问,无论授权状态如何

      • 用于强制应用级权限(仅允许访问少数资源而不是所有资源)

    • 404: 未找到

      • 当客户端请求不映射到任何 API 资源时必须使用
    • 405: 方法不允许

      • 在客户端访问非预期 HTTP 方法时使用

      • 举例来说,只支持读取的资源配置可能只支持GETHEAD,而客户端尝试使用PUTDELETE

      • 请注意,405响应应该是 Allow 头的一部分 (Allow—GET. POST)

    • 406: 不可接受

      • 服务器无法提供请求的媒体类型时必须使用
    • 409: 冲突

      • 用于客户端违反资源状态

      • 例如,当客户端尝试删除非空存储资源时,API 返回此错误

    • 412: 预先条件失败

      • 用于支持条件操作。客户端在请求头中发送一个或多个先决条件,以指示 API 仅执行满足条件的条件;如果不满足,API 应发送412错误代码。
    • 415: 不支持的媒体类型

      • 当 API 无法处理请求的有效负载媒体类型(在内容类型请求头中指示)时必须使用
  • 5xx: 服务器错误: 这些与服务器端错误相关:

    • 500:内部服务器错误

      • 用于报告 API/服务器端问题,并且当这肯定不是客户端的过错时

元数据设计

本节探讨了元数据设计的规则,包括 HTTP 头部和媒体类型。

HTTP 头部

如您可能已经知道,HTTP 规范有一组标准头部,通过这些头部,客户端可以获取有关请求资源的信息,并携带指示其表示的消息,这些消息可能作为控制中间缓存的方向。

以下几点提出了一组符合 HTTP 标准头部的规则:

  • 应在响应中使用 content-type:客户端和服务器依赖于这个头部来指示如何处理消息体,因为 content-type 的值指定了请求或响应消息体中包含的数据的形式,称为媒体类型

  • 应在响应中使用 content-length:客户端应该知道即将读取的消息体的尺寸。另一个好处是,客户端可以知道需要下载的响应体的尺寸,而无需通过发送HEAD请求来下载整个响应。

  • 应在响应中使用 last-modified:响应应指定所需资源表示状态被修改或更新的时间戳,以便客户端和缓存中间件可以依赖此头部来确定其本地副本的资源状态表示的新鲜度。last-modified头部应作为其请求的一部分。

  • 应在响应中使用 ETag实体标签ETag)是一个 HTTP 头部,帮助客户端识别他们请求的资源的具体版本。服务器应该始终在客户端的GET请求中以头部形式响应 ETag。ETag 的值通常是资源内容的摘要(例如,MD5 哈希值),以便服务器可以识别缓存的资源内容是否与资源的最新版本不同。ETag 与last-modified头部的区别在于值(作为摘要的资源内容与时间戳)。此头部值使客户端能够通过在未来的GET请求中使用If-Non-Match条件来选择是否再次发送表示。如果 ETag 值没有变化,那么客户端可以决定在随后的GET请求中节省时间和带宽,不再发送表示。

  • 存储必须支持条件 PUT 请求:REST API 可以通过依赖于带有 If-Unmodified-Since 和/或 If-Match 请求头的客户端请求来支持条件 PUT 请求。由于存储资源使用 PUT 方法进行插入和更新,REST API 应该知道客户端 PUT 请求的意图。PUTPOST 相同,除了 PUT幂等的。请注意,HTTP 支持使用 GETPOSTDELETE 方法的条件请求;这是允许可写 REST API 帮助 API 客户端之间协作的基本模式。

从 RESTful 服务角度来看,服务调用的幂等性意味着客户端发出的调用对所有调用产生相同的结果;也就是说,来自客户端的多个请求与单个请求产生相同的效果。请注意,相同的结果或行为在服务器上。然而,客户端收到的响应可能不与资源状态相同,因为请求之间资源状态可能发生变化。

  • 应使用位置指定新创建资源的 URI(通过 PUT:在通过集合或存储成功创建资源后,API 应提供新创建资源的 URI(位置)作为响应头。位置头可以是状态码 202 响应的一部分,以指导客户端其异步调用的状态。

  • 应利用 HTTP 缓存头:这是为了鼓励缓存,提供缓存控制、过期和日期响应头,以在各种级别上利用缓存,例如 API 服务器端、内容分发网络CDN)或甚至客户端的网络。以下是一些示例:

    • Cache-Control: max-age=90, must-revalidatemax-age 以秒为单位)

    • 对于基于 HTTP 1.0 的缓存,``Date: Tue, 29 Apr 2018 08:12:31 GMT``Expires: Fri, 04 May 2018 16:00:00 GMT

为了阻止缓存,添加带有 no-cacheno-store 的缓存控制头,具体如下:

    • 对于 HTTP 1.0 的遗留缓存

    • 添加 Pragma—no-cacheExpires—0 头值

然而,除非必要,REST API 应始终引发响应的缓存,可能通过较短的持续时间而不是使用无缓存指令来实现。这样,客户端可以通过获取短暂的生命周期响应副本来获得更快的响应,以满足频繁的访问请求。

  • 应使用过期头与 200 ("OK")响应一起使用:对成功的 GETHEAD 请求设置过期缓存头,鼓励客户端进行缓存。请注意,POST 方法也是可缓存的,因此不要将此方法视为不可缓存。

  • 可以使用 3xx 和 4xx 响应的过期缓存头:除了状态码 200 ("OK":成功响应)之外,API 还可以包括 3xx 和 4xx 响应的缓存头,也称为负缓存。这有助于 REST API 服务器通过减少由于某些重定向和错误触发而导致的负载。

  • 不得使用自定义 HTTP 头:自定义 HTTP 头的主要目的是为应用开发者提供额外的信息和故障排除提示;然而,对于服务器端的一些特定情况,除非这些情况不会改变 HTTP 方法的行为,否则它们会很有用。一个例子是使用 X-cache 头部的 API,让应用开发者知道资源是由源服务器还是边缘服务器提供的。如果应该通过自定义 HTTP 头传递的信息是关键的,需要对其请求或响应进行准确解释,那么最好将其包含在请求或响应的主体中,或者在该请求使用的 URI 中。

媒体类型和媒体类型设计规则

正如你在第一章的“资源操作”部分中看到的,RESTful 架构基础简介,媒体类型有助于识别请求或响应消息主体中的数据形式,而内容类型头部的值代表一个媒体类型,也称为多用途互联网邮件扩展MIME)类型。

媒体类型设计影响 REST API 设计的许多方面,包括超媒体、不透明 URI 以及不同和描述性的媒体类型,以便应用开发者或客户端可以依赖 REST API 的自描述特性。

以下简要讨论媒体类型设计规则:

  • 使用特定应用的媒体类型:REST API 将 HTTP 请求或响应的主体视为特定应用交互的一部分。虽然请求或响应主体是用 JSON 或 XML 等语言构建的,但它通常具有需要特殊处理的语义,而不仅仅是解析语言的语法。以下是一个此类 REST API URI 的示例表示,即swapi.co/api/planets/1,它对GET请求响应以 JSON 格式表示的星球大战星球资源。

  • 支持多种表示形式的媒体类型协商:客户端可能需要通过在Accept头中提交所需的媒体类型来要求不同的格式和模式,因此 API 应该允许客户端以所需的格式获取响应。以下是从developer.atlassian.com提供的媒体类型协商示例,针对以下Accept头:

Content-Type: application/json Accept: application/json

观察以下 curl 执行情况:

curl -i -u application_name:application_password --data '{"value": "my_password"}' http://testapi.com /crowd/rest/usermanagement/1/authentication?username=my_username --header 'Content-Type: application/json' --header 'Accept: application/json'  

我们可以得出以下结论:

  • 使用查询参数支持媒体类型选择:

    • 为了支持具有简单链接和调试功能的客户端,REST API 应该通过名为accept的查询参数支持媒体类型选择,其值格式与Accept HTTP 请求头中的格式相匹配

    • 例如,REST API 应优先采用更精确和通用的方法,如下所示媒体类型,使用GET https://swapi.co/api/planets/1/?format=json查询参数标识,而不是其他替代方案

Windows 操作系统用户可以使用 MobaXterm (mobaxterm.mobatek.net/) 或任何支持 Unix 命令的 SSH 客户端。

表示

如我们所知,资源当前状态的机器可读描述,无论是请求还是响应,都是表示,它可以采用不同的格式。以下部分讨论了最常见资源格式(如 JSON 和超媒体)以及错误类型的简要规则。

消息体格式

在分布式环境中,REST API 通信通常以文本格式进行,我们将讨论以下 JSON 文本格式表示规则:

  • 使用 JSON 进行资源表示,并且应该格式良好

  • 您也可以使用 XML 和其他格式

  • 不要创建额外的封装或任何自定义传输封装,仅利用 HTTP 封装

超媒体表示

如我们从第一章,《RESTful 架构基础介绍》所理解,REST API 客户端可以使用统一链接结构作为 HATEOAS 响应进行编程导航,以下是一些与超媒体表示相关的规则。

以下截图帮助我们回忆第一章,《RESTful 架构基础介绍》,的 HATEOAS 表示:

图片

这也可以帮助我们关联以下规则:

  • 使用一致的形式来表示链接、链接关系和链接公告

  • 在响应消息体中提供一个自链接表示

  • 最小化广告的入口点或 API URI 的数量

  • 使用链接以状态敏感的方式宣传任何资源操作

媒体类型表示

GET请求不会包含请求体,响应体始终是资源表示。因此,对于除GET请求之外的所有客户端请求,API 应在请求体和响应体中定义媒体类型。API 媒体类型与排序、过滤、分页和链接等特性相关。因此,媒体类型格式和模式应保持一致。

错误表示

HTTP 方法的错误状态码(4xx 和 5xx)可以在响应体中携带客户端可读信息。以下规则展示了错误和错误响应的一致形式:

  • 错误和错误响应应保持一致

  • 通用和常见错误条件的错误类型也应保持一致

以下图表展示了一个示例 JSON 响应,并说明了错误和错误代码如何在 API 响应中占主导地位:

图片

客户端关注点

如我们从API 设计最佳实践部分所知,REST API 世界的 REST API 客户端是 APP 开发者,REST API 旨在满足其客户端程序(APP 开发者代码)的需求。本节讨论一系列 REST API 设计原则,以解决常见的客户端关注点。

版本化

REST API 是一组相互链接的资源或资源模型。表示性资源通过版本传递其状态。因此,版本化是基本设计原则之一,在陈述版本化规则之前,我们将查看 API 的版本化。

当 API 经历破坏性更改时,应该对其进行版本化(增加主版本);破坏性更改包括以下内容:

  • 响应数据更改

  • 响应类型更改

  • 移除 API 的任何部分

即使涉及较小的或非破坏性修改,如添加新端点或新响应参数,API 也应进行版本更改。小版本有助于跟踪 API 的小变化,并协助客户支持,他们可能正在接收缓存的数据版本或可能遇到其他 API 问题。以下是关于 REST API 版本化的几个规则:

  • 使用新的 URI 引入新的模型或概念

  • 使用模式来管理表示形式版本

  • 利用 ETags 来管理表示状态版本

通用版本化实践遵循方案版本化(semver.org/);然而,RESTful API 中的版本化实践引起了大量讨论,请注意,作为 API 设计者,我们可能需要根据业务需求和影响做出决策。

安全性

REST API 可能会暴露包含仅限于特定客户端查看的安全信息的资源,以下规则有助于保护这些资源:

  • 使用基于 HTTP 的授权协议 OAuth 来保护资源

  • 使用 API 管理解决方案,如反向代理,来保护资源

我们在下一章提供了一个示例实现。

响应表示组合

当 REST API 随着新功能的发展而演变时,客户端可能需要从其支持的 REST API 获取新的资源。然而,出于许多实际原因,客户端可能需要从 REST API 获取新的资源。因此,REST API 可以提供一种控制其响应表示组成的措施。REST API 保持一致的资源模型设计至关重要,这样客户端才能从组合响应中受益。以下两个规则使客户端能够调整响应:

  • 应该支持通过使用 URI 的查询组件进行部分响应

  • 应该通过使用 URI 的查询组件嵌入链接资源

例如,您可以参考本章的URI 查询部分。

处理超媒体

我们之前讨论了两种超媒体结构,链接和链接关系。它们帮助客户端使用一致的算法处理响应结构。

客户端应与特定的 REST API 响应表示链接进行交互。以下要点讨论了一个简单的流程:

  • 客户端处理程序首先通过其关系名称查找链接,并使用适当的 HTTP 请求方法与链接查找进行交互。

  • 客户端代码检查链接关系文档资源的 method 字段,以决定内容是否应该提交到请求消息体中。

JavaScript 客户端

最常见的情况是,JavaScript 客户端希望与 REST API 交互。然而,网络浏览器对同源(也称为同源策略)施加了限制(沙盒化)。它限制了 JavaScript 客户端访问资源,如果资源不是来自同一域/自身来源。URI 方案、主机和端口组件指示资源来源是否来自同一域。浏览器实施同源策略以防止泄露机密用户数据。

然而,在大多数情况下,REST API 需要为其 JavaScript 客户端提供从 JavaScript 的多源读写访问,我们将看到以下规则如何实现这种灵活性:

  • 支持从 JavaScript 使用带填充的 JSONJSONP)进行多源读取访问

  • 支持跨源资源共享CORS)以提供从 JavaScript 的多源读写访问

摘要

作为设计策略的一部分,我们探讨了 API 是什么以及它在数字化时代连接各种设备和技术的意义。我们考察了一些基本的 API 设计特性,如可用性、利用现有网络基础设施以及 API 设计目标,例如一致性、简化、灵活且稳定的 API,以及 API 设计者如何关注 APP 开发者——API 的消费者。

我们还简要讨论了 API 设计者的角色以及他们需要遵循的设计原则和规则,以便他们能够创建更好的 RESTful API。我们通过给出 RESTful API 的必要规则以及一些应该做和不应该做的事情来结束本章。

因此,我们在下一章为你打下了坚实的基础和路径,以便你能够掌握 API 编程,因为它们涉及实际的 REST API 程序实现,以及各种 RESTful API 设计模式和应用程序。

进一步阅读

我们鼓励读者参考www.packtpub.com/all以获取各种参考资料和大量关于 RESTful 服务和 API 的书籍。

第三章:重要的 RESTful API 模式

本章提供了 RESTful API 常见和基本设计模式的概念和代码示例,以便您可以挑选这些示例并复制和增强它们的 RESTful API 服务。

由于设计模式提供了通用的、经过时间考验的、经过验证的和可重用的解决方案,以解决熟悉但反复出现的设计问题,因此 API 设计模式对于软件设计师在他们的 RESTful API 应用中学习和适应是必不可少的。API 设计模式提供了解决特定、反复出现的 API 设计问题的描述或模板,任何软件架构师和 API 设计师都希望在他们的 API 设计中采用。采用模式为开发者提供了很大的灵活性,并帮助他们专注于业务逻辑实现,以高质量交付服务。

作为本章的一部分,我们将学习以下常见且重要的 API 设计模式,以及一些代码示例。然而,请注意,以下模式没有特定的顺序,每个模式都针对 RESTful 约束。我们还需要确保这些基本模式在我们的 API 设计和实现模式中得到了考虑和内化:

  • 无状态

  • 内容协商

  • URI 模板

  • 针对意图进行设计

  • 分页

  • 可发现性

  • 错误和异常记录

  • Unicode

技术要求

由于我们将在本章中深入代码和示例,以及一些模式实现,因此读者应具备 Java 编程语言的知识并理解 Java 1.8 的基本概念。我们的示例使用 Spring Boot 实现,并提供了在任何地方下载和运行示例代码的说明。然而,对于那些想要执行和测试本章提供的代码示例的人来说,他们可能需要具备对数据格式(如 JSON 和 XML)的基本和必要的理解,以及对于 Maven 构建过程和客户端/服务器或 Web 服务开发的基本理解。

以下为本章的 GitHub 链接:github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices.git. 

要在您的计算机上运行代码,您需要 Java 8、Spring 4(1.4.4)和 Maven 3.x。请按照以下说明开始。以下为本章的先决条件:

  • Java 1.8

  • Maven 3.2

  • 您喜欢的 IDE(可选)

  • 命令行

  • Postman

首先,请从 GitHub 下载或克隆示例。如果有人需要帮助将示例下载到本地机器,有多种在线帮助资源可用。

从安装开始

如果您环境中还没有安装 JDK 或 Maven,您可能需要现在按照它们各自的安装说明进行安装。我们需要在我们的机器上安装 Maven,并且我们还可以使用 IDE 运行程序。以下说明涵盖了如何使用 Windows 命令行和 maven 命令行运行这些示例。

如果您需要 Maven 的安装说明,请按照安装文档中定义的链接和步骤进行操作。Maven 安装指南可在maven.apache.org/install.html找到。

以下部分提供了如何运行本章示例的说明,以及作者在其基于 Windows 的笔记本电脑上能够设置和运行的截图:

图片

图片

图片

如果您需要安装 Postman 的帮助,您可能会发现此链接很有用:learning.getpostman.com/docs/postman/launching_postman/installation_and_updates/。如果您需要帮助将 Postman 集合导入到您的本地 Postman 安装中,您可能会发现此链接很有用:learning.getpostman.com/docs/postman/collections/data_formats/#exporting-and-importing-postman-data

从 RESTful API 模式开始 - 第一部分

本章涵盖了最常见和必要的 REST 服务设计模式,以帮助 API 设计师和开发者在各个领域的 API 开发中。本节包含的设计模式如下:

  • 无状态

  • 内容协商

  • URI 模板

  • 设计意图

  • 分页

  • 可发现性

  • 错误和异常记录

  • Unicode

无状态

无状态指的是服务器摆脱应用状态,即存储在服务器端并帮助识别客户端请求、客户端最后交互细节及其当前上下文信息的状态。

REST 架构强制服务器在服务器端维护任何客户端状态,并坚持服务器与调用客户端之间的无状态性。任何 API 开发者当然不希望在应用服务器端存储状态信息。因此,应用服务器应该始终设计为无状态(在大多数情况下)。

让我们观察客户端和服务器的一些责任,以便我们实现无状态性:

客户端 服务器
客户端应将其请求中所有必要的信息提供给服务器。 服务器理解客户端的请求,并应在响应中包含客户端创建会话所需的所有必要信息。
会话状态应完全由客户端管理和保持。 服务器不存储客户端状态,也不依赖于其存储的上下文。
客户端负责存储和处理所有其状态,并在需要时将状态信息发送到服务器。 服务器上不维护任何会话亲和性或会话粘性。

为了遵守 RESTful 约束,并且为了服务真正实现无状态,服务器甚至不存储客户端的认证/授权信息,并要求客户端在请求中提供凭证。因此,服务器会单独理解每个请求,并且之前的请求对当前请求没有影响。

在我们章节的代码示例中,我们可以观察到,我们的所有请求和响应都与/携带任何状态信息无关,它们是完全独立的。即使在后面的章节中,当我们开发认证示例时,我们的代码仍然会发展和保持在其生命周期中的无状态性。

对于无状态约束,我们如何编码?Spring Boot 的 REST API 框架提供了现成的实现;作为开发者,我们的责任是确保我们遵循 URI 约束,并为 URI 提供必要的实现。在以下代码片段中,来自我们的示例,InvestorController.java,我们定义了一个 URI (/investors/{investorId}/stocks ),用于通过投资者 ID 获取投资者的股票;仅此而已——我们没有任何特定的实现,会话验证等:

@GetMapping(path = "/investors/{investorId}/stocks")
public List<Stock> fetchStocksByInvestorId(@PathVariable String investorId,
  @RequestParam(value = "offset", defaultValue = "0") int offset,
  @RequestParam(value = "limit", defaultValue = "5") int limit) {
    return investorService.fetchStocksByInvestorId(investorId, offset, limit);
}

由于路径元素没有任何状态指示,并且代码期望一个investorId以及一些其他参数,我们的实现满足了无状态约束。这将在我们处理下一章中的身份验证(也是无状态的)时更有趣。

让我们通过以下表格观察强制 RESTful 服务无状态化的必要性和一些优点:

优点 细节
可扩展性 通过将服务器代码部署到多个服务器上,实现可扩展性,这样任何服务器都可以处理任何请求,因为服务器不需要维护会话粘性/亲和力。这是因为客户端的请求将包含服务器管理所需的所有必要信息。
简化复杂性 由于服务器可以摆脱服务器端状态同步逻辑和实现(导致简化了应用程序设计),因此复杂性降低。
易于缓存,因此性能提升 中间件软件可以通过查看客户端请求来缓存特定 HTTP 请求的结果。此外,关于先前请求的状态信息没有不确定性和担忧。
可追踪性 由于客户端请求本身包含所有必要信息,服务器永远不会失去对每个客户端在应用程序中位置的跟踪。
最佳使用 HTTP/HTTPS 由于 HTTP 本身是一个无状态协议,REST 实现与 HTTP 协议无缝结合(它很好地遵循 REST 约束)。

内容协商

RESTful API 中的资源需要处理不同类型的表示,不仅仅是 XML 或JavaScript 对象表示法JSON),因为不同的客户端可能需要不同的表示。实际上,随着我们构建复杂 API,我们可能会发现 XML/JSON 过于限制,我们可能需要转向另一种完全不同格式的内容(Instagram 和 Flickr 使用 JPEG 和 PNG 图像,而媒体机构使用 MP3/MP4),这就是我们如何进行内容协商的。

内容协商是一种机制或过程,服务和客户端可以在它们通常的通信过程中选择作为它们资源表示格式的通信和握手。

正如我们在第二章,“设计策略、指南和最佳实践”,在HTTP 头部分所看到的,HTTP 规范提出了一组标准头,客户端可以通过这些头获取有关请求资源的信息,并携带指示其表示的消息。

因此,对于内容协商,REST 服务需要使用 HTTP 头;也就是说,当客户端发起请求时,它包括接受头,客户端和服务器可以处理的文件类型列表,无需对客户端请求、服务器处理和回复采取额外步骤。

如果响应表示的选择是在服务器端确定的,那么它是服务器驱动的协商或使用请求头的主动协商。如果同样的选择是在客户端,那么它是代理驱动的内容协商或使用不同 URI 的反应式协商。对于大多数实际用途,服务器端协商更为复杂,并且需要对客户端需求做出许多假设。因此,大多数 REST API 实现遵循代理驱动的内容协商,这依赖于 HTTP 请求头或资源 URI 模式。

让我们通过以下链接快速查看一个内容协商的实时例子:www.w3.org/StyleSheets/TR/logo-REC。观察一下,logo-REC 是一个图像文件;然而,它没有任何文件扩展名(即,w3.org 在没有任何文件后缀的情况下提供图像),而 log-REC 不仅仅是一个文件,而是两个——loge-REC.giflogo-REC.png。因此,通过内容协商,w3.org 服务器提供了两个不同的文件。以下截图解释了更多关于相同请求和响应头的信息:

请注意前一个截图中突出显示的部分。URL 路径,www.w3.org/StyleSheets/TR/logo-REC,没有说明任何文件扩展名;然而,在响应头中,观察content-locationcontent-typeimage/png。突出显示的矩形是一些内容协商的快速示例。同时,请观察通过请求头 accept 进行的反应式协商。

让我们接下来详细讨论内容协商以及服务/客户端如何使用它们。

使用 HTTP 头进行内容协商

通过我们之前的例子,我们已经看到服务器理解了传入请求和具有 HTTP 请求头内容类型的实体的内容类型。

在我们的代码示例中,我们实现了以下内容类型,默认为application/JSON,并且为了表示客户端希望获取的内容类型,我们使用application/JSON作为 Accept 头。

请注意,如果请求中没有 Accept 头,服务器将发送预先配置的默认表示类型。在我们的例子中,它始终是application/JSON,如下面的截图所示:

上述截图展示了我们例子中的 Content-Type 和 Accept 头。

如果我们想在我们的投资者服务应用程序中以类似的方式实现我们之前提到的 w3c 图像示例,我们只需要在pom.xml中添加以下依赖项:

<dependency> 
  <groupId>com.fasterxml.jackson.dataformat</groupId> 
  <artifactId>jackson-dataformat-xml</artifactId> 
</dependency> 

按照以下方式修改控制器类中的@GetMapping注解:

@GetMapping(path="/investors/{investorId}/stocks/{symbol}", produces={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})

因此,通过使用 accept 请求头,客户端要么以 XML 形式,要么以 JSON 形式获取响应。酷,不是吗?

使用 Postman,我们将根据application/XMLapplication/JSONAccept头信息值获取 XML 或 JSON 响应:

我们可以将内容类型信息传递给服务器的两种其他方式如下:

以下表格提供了一个快速参考指南,其中包含一些其他内容协商示例,因为服务器和客户端可能需要处理许多其他内容协商方面:

内容协商 实现
表明客户端偏好——允许客户端表明其能力(如媒体类型和语言) Accept:application/json,application/xml;q=0.9,*/*;q=0.8 (支持 JSON 或 XML,q -> 偏好顺序)
实现媒体类型——如何决定在响应中表示中应使用哪种媒体类型 根据客户端的Accept头信息(Accept: application/atom+xml),服务器可以响应一个内容类型,即Content-Type: application/atom+xml; charset=UTF-8
实现字符编码——了解在响应中的文本表示应使用哪种字符编码 Content-Type: charset=UTF-8 (使用 content-type charset)
支持压缩——了解何时启用表示的压缩 # 请求Accept-Encoding: gzip# 响应Content-Encoding: gzip Vary: Accept-Encoding
发送 Vary 头信息——了解如何使用 Vary 头向客户端指示服务器如何选择特定的表示 # 响应Content-Language: en Vary: Accept-Language
处理谈判失败——了解何时提供默认表示或返回错误 # 请求Accept: application/json,*/*;q=0.0 (客户端只能处理 JSON 格式的内容)# 响应406 Not Acceptable (服务器返回错误代码,因为它不支持 JSON。@GetMapping我们的章节示例在客户端期望只有 XML 响应时抛出此错误,因为我们的示例提供的是 JSON 而不是 XML,除了一个GET映射)链接:<http://www.example.org/errors/mediatypes.html>;rel="help"{"message": "此服务器不支持 JSON。请参阅帮助了解替代方案。"} (额外帮助)

URI 模板

如我们在第一章,“RESTful 架构基础介绍”中看到的,统一资源标识符URI)通常用于在类似资源的公共空间中标识特定的资源。例如,如果我们从第一章“RESTful 架构基础介绍”中选取星球大战 API 示例,电影资源由以下 URI 表示:swapi.co/api/films/2swapi.co/api/films/3,等等。客户端通常需要在他们的请求中包含一些附加信息,以及服务器如何让客户端在 URI 中包含有关资源的信息。服务器端开发者需要能够描述其服务将响应的 URI 布局。

这个问题的答案是 URI 模板。URI 模板提供了一种描述一组资源作为变量的方式。因此,在我们继续前进并查看这一章的代码示例之前,让我们观察以下表格,其中包含我们的更通用示例:

资源 URI 模板
人物:swapi.co/api/people/ 行星:swapi.co/api/planets/ 电影:swapi.co/api/films/ 物种:swapi.co/api/species/ https://swapi.co/api/{resource_id}/
swapi.co/api/films/2/swapi.co/api/films/6/ https://swapi.co/api/{resource_id1}/{resource_id2}

因此,很明显,如果我们需要为上述列表定义带有变量或资源标识符的 URI 模板,我们需要在花括号内提供这些变量。

现在,我们将看到来自本章代码实现的示例,这些示例是从投资者服务控制器类中挑选出来的:

@GetMapping("/investors/{investorId}/stocks") @GetMapping("/investors/{investorId}/stocks/{symbol}") 客户需要发送一个投资者 ID 和一个股票符号以进行GET操作
@DeleteMapping("/investors/{investorId}/stocks/{symbol}") 客户需要发送一个投资者 ID 和一个股票符号以进行Delete操作

Spring Boot 的@PathVariable注解为客户端需要的资源应用 URI 模板的魔法。请注意以下代码片段(来自我们的代码InvestorController.java)作为示例:

........ 
.......... 
public ResponseEntity<Void> updateAStockOfTheInvestorPortfolio( 
    @PathVariable String investorId,  
    @PathVariable String symbol,  
    @RequestBody Stock stockTobeUpdated)  
{ 
          Stock updatedStock = investorService.updateAStockByInvestorIdAndStock(investorId, symbol, stockTobeUpdated); 
.......... 
........ 

Spring Boot 提供的@PathVariable注解帮助我们无缝地实现 URI 模板模式。正如我们所见,investorId和符号变量被我们的方法作为参数提取。

请注意,还有其他 REST 框架做得很好,并提供开箱即用的 URI 模板功能。

设计意图

想象一下我们的汽车在没有送到汽车服务中心修理的情况下被修理。几年前,汽车制造公司特斯拉在向其汽车发布空中修复时,就著名地做到了这一点。特斯拉的软件更新以检测充电问题并帮助降低充电速率(级联效应),以避免过热并因此避免发动机火灾。

意图设计是一个在结构和汽车领域用于参数化更改的术语,我们将学习它如何使 REST API 服务受益。

意图设计是一种表达对象之间不同关系的方法,以便一个对象的变化会自动传播到其他对象。在特斯拉的案例中,充电周期数量的减少(级联效应)有助于避免发动机过热。

在 RESTful API 世界中,API 应该被开发以确保它们满足用户提供的和面临的使用案例的要求,但又不暴露内部业务对象。意图设计是一种旨在影响或导致特定和额外用户行为的战略设计模式。

为了将我们投资者服务示例中的意图实现设计联系起来,我们应该提供一种机制,以便在新的(类型)股票被添加到投资者对象时自动更新投资者的投资组合:

图片

正如您在上述图中看到的,API 的意图是添加一个新的股票;然而,它也应该对投资者的投资组合产生级联效应——也许应该添加新的股票类型,需要更新股票的总数,等等。这抽象于应用开发者。当删除股票时也是如此。

请回忆一下第二章中关于 API 粒度的讨论,设计策略、指南和最佳实践,因为选择正确的 API 粒度在意图设计策略中起着至关重要的作用。在本章的示例中,我们提供了一个简单的更新方法,用于向投资者的投资组合添加股票类型,并鼓励读者开发更多功能,因为这种模式确保了具有正确的 API 粒度(粗粒度与细粒度)来更新和删除投资者投资组合中的股票类型。考虑以下代码:

........ 
public Stock addNewStockToTheInvestorPortfolio(....) { 
  if (......)) { 
      designForIntentCascadePortfolioAdd(investorId); 
      return ......; 
  } 
...... 
} 
......... 
......... 
public boolean deleteStockFromTheInvestorPortfolio(....) { 
  ...... 
  if (....) { 
     .... 
     designForIntentCascadePortfolioDelete(investorId, deletedStatus); 
    return .......; 
      } 
......... 

private void designForIntentCascadePortfolioAdd(...) { 
            ..... 
      } 
private void designForIntentCascadePortfolioDelete(...) { 
            ........; 
      } 
} 

上述代码的输出如下:

图片

上述截图和代码是我们在InvestorService.java中可以看到的示例实现(代码片段)。这也显示了当使用 Postman 工具调用DeleteAdd API 时,控制台消息的情况。

分页

当客户端尝试获取一个分页的对象列表时,我们必须考虑服务器如何在不损害其性能的情况下管理向客户端提供如此巨大的响应。

分页是一个概念,它有助于仅作为响应提供部分数据,然而,它提供了如何从服务器逐页访问所有数据的信息,从而减少了服务器提供全部数据的负载和计算量。

我们是否应该将页面(的结果)视为资源或仅仅是资源的表示?将页面视为表示而不是资源是我们将在本节中讨论的内容。

由于我们决定分页是资源表示的一部分,我们将分页信息包含在 URI 查询中,即xxx.api.com/stocks?page=2

请注意,作为 URI 路径一部分的分页不是一个选项(因为我们认为它不是一个资源,而是资源表示),即xxx.api.com/stocks/page/2,因为我们可能无法在调用之间唯一地找到资源。

在处理 URI 查询的分页时,我们需要解决的一个问题是编码,我们可以使用标准的编码方式来编码分页信息。

在我们深入代码之前,让我们看看一些行业中的更好的 API 分页示例以及一些分页类型。

Facebook 的 API 使用偏移量和限制(fields=idmessage& amp;limit=5),LinkedIn 使用起始和计数(.../{service}?start=10&count=10),而 Twitter 使用每页记录数或计数(api.twitter.com/2/accounts/abc1/campaigns?cursor=c-3yvu1pzhd3i7&count=50)。

分页的资源表示方式有三种变体,具体如下:

  • 基于偏移量的: 当客户端需要基于页数和页码的响应时。例如,GET /investor/{id}/stocks?offset=2&limit=5 (返回 2 到 7 的股票)

  • 基于时间的: 当客户端需要在指定的时间范围内获得响应,并且可以有一个限制,以及作为参数的一部分来表示每页的最大结果数。例如,GET /investor/{id}/stocks?since=xxxxxx&until=yyyyy (返回给定日期之间的股票).

  • 基于游标的: 一种技术,其中指针(一个带有面包屑的内置书签)引用剩余数据,作为响应提供特定子集的数据,然后释放。然而,直到游标到达记录的末尾,剩余的数据仍然需要用于后续请求。例如,GET slack.com/api/users.list?limit=2&token=xoxp-1234-5678-90123。以下代码解释了这一点:

@GetMapping(path = "/investors/{investorId}/stocks") 
  public List<Stock> fetchStocksByInvestorId( 
      @PathVariable String investorId,  
      @RequestParam 
      (value = "offset", defaultValue = "0") int offset, 
      @RequestParam 
      (value = "limit", defaultValue = "5") int limit) { 

  return investorService.fetchStocksByInvestorId(investorId, offset, limit); 
      } 

上述代码的输出如下:

前面的代码块展示了在我们投资者服务代码中的基于偏移量的分页实现。我们将在下一章讨论版本化和涉及数据库的其他模式时,实现和讨论其他分页方法,包括排序和过滤。

可发现性

正如我们在前面的章节中讨论的那样,API 开发者是 API 存在的理由。帮助他们找到合适的 API,并帮助他们通过编程方式确定正在访问的网站是否启用了 API,将是 API 最关键的责任。

与网站建立连接的主要步骤是通过使用作为用户输入的简单 URL 来找出该网站是否启用了 API。它们还帮助我们了解如何访问它们。

API 的可发现性完全取决于服务器描述其 API 使用方式的描述能力。

让我们在下一节中查看两种类型的可发现性及其在我们代码示例(截图)中的实现:

  1. 通过有效的 HTTP 方法:当客户端使用无效的 HTTP 方法调用 REST 服务时,该请求的响应应最终以405HTTP 错误代码结束;即405 方法不允许。除了错误代码外,响应头还应提供灵活性,使客户端能够找到其响应中允许的允许的方法。此代码如下:
@DeleteMapping("/investors/{investorId}/stocks/{symbol}") 
public ResponseEntity<Void> deleteAStockFromTheInvestorPortfolio( 
  @PathVariable String investor, 
  @PathVariable String symbol) { 
    if (investorService.deleteStockFromTheInvestorPortfolio(investorId, symbol)) { 
      return ResponseEntity.noContent().build(); 
    } 
    return ResponseEntity.ok(null); 
} 

上述代码的输出如下:

图片

  1. 通过提供新创建资源的 URI:将 URI 作为部分位置头作为新创建资源的响应是另一种可发现性的方法。返回的 URI 作为位置将通过GET可用。此代码如下:
@PostMapping("/investors/{investorId}/stocks") 
public ResponseEntity<Void......) { 
  Stock insertedStock = investorService.addNewSto...; 
  if (insertedStock == null) { 
    return ResponseEntity.noContent().build(); 
  } 
  URI location = ServletUriComponentsBuilder..... 
  return ResponseEntity.created(location).build(); 
} 

上述代码的输出如下:

图片

请注意,由于我们的示例使用的是 Spring Boot,我们正在利用 Spring Boot 无缝和开箱即用的可发现性实现能力(使用@GetMapping、servlets URI 组件构建器等)。

另一种类型的可发现性(尽管尚未标准化)可以通过有效的链接头实现。当通过GET响应客户端的特定资源时,你必须向客户端提供有关他们下一步可以做什么的线索;也就是说,通过链接头提供所有可用资源列表:

图片

作为可发现性的一个实时示例,前面的截图展示了谷歌的一个 API。

错误和异常日志记录

当我们讨论 API 开发者对我们服务消费的重要性时,我们一直在强调,错误处理和异常处理应无妥协地得到妥善处理。服务对 API 开发者来说是黑盒子,因此提供错误和异常的服务可以为客户端提供清晰的上下文和可见性,以便使用我们的 API。

让我们看看一些真实世界且非常流行的 API,以及它们在样本错误场景中是如何处理这些错误的。这将在以下表格中展示。我们还将查看我们在投资者服务代码中处理这些错误和异常的方式:

APIs HTTP 代码 样本消息 备注
Facebook 200 {"type":"OAUTH exception","message":"...."} 注意,200表示成功。然而,API 决定发送一个错误消息作为响应,同时仍然为 API 调用提供一个成功(200)代码。
Twilio 401 {"status":401,"message":"Authenticate","code":"...."..} 利用现有的 HTTP 代码。
投资者服务 405 {"Status":405,"error":"Method Not Allowed".... 利用现有的 HTTP 代码。

在我们的样本仓库中,我们还实现了一个名为InvestorNotFoundException的自定义异常类的示例。以下是从 Postman 获取的代码片段和输出样本:

public class InvestorNotFoundException extends RuntimeException{ 

      ........ 
      public InvestorNotFoundException(String exception) 
      { 
            super(exception); 
      } 
} 

上述代码的输出如下:

图片

从我们之前的例子中,我们可以观察到我们在使用现有的 HTTP 标准错误代码管理错误和异常方面做得相当不错。然而,我们可以更进一步,通过向调用者提供更多定制化的错误和消息来做到这一点;例如,调用者收到一个404错误而不是500错误可能更为恰当。也许我们将在下一章构建更多模式的同时实现一些自定义错误消息。

Unicode

使我们的 API 支持多种语言的一个简单而强大的方法是使 API 能够支持 Unicode。

Unicode是一种编码标准,它支持国际字符集。它为包括中文、韩文和阿拉伯文及其脚本在内的多种语言中的每个字符都有一个唯一的数字。这个唯一的数字使得几乎所有的字符都可以在平台、程序和设备之间识别和访问。

因此,简而言之,我们可以通过支持 Unicode 作为它们头部的部分来简化支持多语言的 REST API。以下代码展示了这一点:

@GetMapping(value="/investors/welcome", produces="text/plain;charset=UTF-8") 
  public String responseProducesConditionCharset() {    return " (\"Welcome Investor!\" in Japanese)"; 
} 

其输出如下:

图片

以下图表显示了(InvestorController.java)头部中的 accept-encoding 字符集代码片段以及 Postman 的结果。

摘要

在了解了处理无状态、内容协商实践、URI 模板定义、服务设计以实现意图、可发现性和一种分页类型等真实代码示例之后,我们详细讨论了错误和异常处理,最后以 Unicode 国际化实现(支持我们的服务中的多种语言)作为结论。

本章应为任何想要掌握 RESTful 服务的人提供一个出色的起点;不仅包括基础知识,还包括核心模式。在下一章中,我们将看到更多高级模式的实现和示例,以便我们的读者能够越来越多地利用最佳实践和实现。

第四章:高级 RESTful API 模式

每个软件设计师都认同,在现代软件设计和开发的生命周期中,设计模式和通过实现设计模式来解决熟悉且反复出现的设计问题是不可避免的。一般来说,存在各种 API 设计模式,在我们前面的章节中,我们介绍了几种基本的 RESTful API 模式。在本章中,我们将深入探讨一些高级 API 设计模式,因为这些模式对于实时 RESTful 服务和当前软件行业的需求是必要的。

本章是 API 设计模式的第二部分;目的是介绍多个高级设计模式,例如版本控制和后端前端。一旦读者阅读了本章,他们应该知道如何实现以下模式:

  • 版本控制

  • 授权

  • 统一契约

  • 实体端点

  • 端点重定向

  • 幂等性

  • 批量操作

  • 断路器

  • API 外观

  • 后端前端

请不要忘记查看投资者服务代码示例,并学习高级模式实现。

技术要求

由于本章涉及高级模式,我们期望读者具备一些基本软件设计模式和惯例的知识,Java 编程能力,Spring 框架和 RESTful 服务。

要运行本章的示例代码,请参阅第三章,《必要的 RESTful API 模式》,以获取额外的技术要求和有关如何运行此处提供的示例代码的信息。

RESTful API 高级模式

在前面的章节中,我们介绍了一些关键的 RESTful 模式;现在是时候深入研究更高级的模式,并动手为我们客户提供尽可能好的 RESTful 服务实现。让我们开始学习如何为我们服务实现版本控制。

版本控制

许多书籍和文章建议如果可能的话避免对 API 进行版本控制。然而,相信我们会在第一次发布时开发出一个几乎满足所有需求的 API,并且永远不会改变是不切实际的,所以我们完全避免版本控制。还有一些人建议为不同的(主要)版本变化提供不同的 URI。理想情况下,我们会像管理网站 URL 一样管理 API(大多数情况下,URL 地址不会改变,无论有任何变化/实现)。

我们在版本控制 API 时希望遵循的一般规则如下:

  • 当新的实现破坏现有的客户实现时,将 API 升级到新的主要版本。

  • 当新的实现提供了增强功能和错误修复时,将 API 升级到 API 的新次要版本;然而,确保实现考虑了向后兼容性,并且不会影响现有的客户实现。

让我们考虑一下,我们需要在我们的 API 中管理版本;我们有四种不同的方式可以实现版本控制,我们将在以下各节中看到每种类型。

通过 URI 路径进行版本控制

主版本和次版本变更可以是 URI 的一部分,例如,为了表示 API 的v1v2版本,URI 可以是http://localhost:9090/v1/investorshttp://localhost:9090/v2/investors

我们投资者服务示例中 URI 路径版本控制的代码实现如下所示:

@GetMapping({"/v1/investors","/v1.1/investors","/v2/investors"})
  public List<Investor> fetchAllInvestors()
    {
       return investorService.fetchAllInvestors();
    }

这段代码的输出如下:

以下截图显示了 Postman 执行的 URI 路径版本控制示例。

根据版本更改 URI 路径违反了 RESTful 原则的 URI 及其资源表示(两个不同的 URI 表示相同的资源——唯一的区别是我们例子中的v1v2等)。然而,由于实现简单,URI 路径版本控制是管理 API 版本的一种流行方式。

通过查询参数进行版本控制

实现版本引用的另一种简单方法是将它作为请求参数的一部分,如下面的示例所示——http://localhost:9090/investors?version=1http://localhost:9090/investors?version=2.1.0

@GetMapping("/investors")
public List<Investor> fetchAllInvestorsForGivenVersionAsParameter(
@RequestParam("version") String version)
throws VersionNotSupportedException {
if (!(version.equals("1.1") || version.equals("1.0"))) {
throw new VersionNotSupportedException("version " + version);
}
return investorService.fetchAllInvestors();
}

如下是输出结果:

以下截图显示了在样本中通过参数进行版本控制的实现。

通过自定义头部进行版本控制

定义一个新的头部,该头部包含请求中的版本号,作为请求头部的一部分。自定义头部允许客户端在版本升级的情况下保持相同的 URI。这种实现方式是我们之前在第二章中看到的,设计策略、指南和最佳实践,我们将在下一节中再次看到。以下代码片段将帮助我们通过名为x-resource-version的自定义头部理解版本实现。请注意,自定义头部的名称可以是任何名称;在我们的例子中,我们将其命名为x-resource-version

@GetMapping("/investorsbycustomheaderversion")
public List<Investor> fetchAllInvestors...(
@RequestHeader("x-resource-version") String version)
throws VersionNotSupportedException {
return getResultsAccordingToVersion(version);
}

上述代码的输出如下:

以下截图是使用x-resource-version自定义头部进行版本控制并使用 Postman 执行相同代码示例的示例。

通过内容协商进行版本控制

通过响应中的Accept(请求)头部以及内容类型(媒体)提供版本信息是首选方式,因为这有助于在不影响 URI 的情况下对 API 进行版本控制。正如我们在第二章中已经学到的,设计策略、指南和最佳实践,让我们跳转到通过 Accept 和 Content-Type 进行版本控制的代码实现:

@GetMapping(value = "/investorsbyacceptheader",
headers = "Accept=application/investors-v1+json,
application/investors-v1.1+json")
public List<Investor> fetchAllInvestorsForGiven..()
throws VersionNotSupportedException {
return getResultsAccordingToVersion("1.1");
}

下面的截图是前面代码的输出:

图片

前面的截图展示了通过 Accept 头执行我们的投资者服务应用版本控制的过程。请同时注意响应头中的内容类型。

正如我们所见,每种版本控制方法都有其优缺点,因此我们需要根据具体情况确定正确的方法。然而,内容协商和自定义头是 RESTful 服务的支持者。我们将继续讨论一个重要的模式:认证和授权。

授权

到目前为止,我们已经构建了一个包含各种集成模式的示例应用投资者服务。现在,我们如何确保我们的 REST API 实现仅对真实用户开放,而不是对所有用户开放?在我们的例子中,投资者列表不应对所有用户可见,而股票 URI 不应向除合法投资者以外的任何人公开。这时,授权头就派上用场了。我们将使用一种称为基本认证的方案,因为它解决了我们当前的需求。请注意,有不同方案可供选择,例如基本认证、基于哈希的消息认证(HMAC)、JSON Web TokenJWT)和 OAuth 2.0 携带者认证令牌方案,这些方案都可以用于保护 REST API。然而,在本节中,我们将通过授权头实现简单的基本认证,并在第六章中详细讨论 OAuth 2.0,RESTful 服务 API 测试和安全

让我们从基本认证的简单概述开始。它是一个标准的 HTTP 头(符合 RESTful API 约束),用户的凭据以 Base64 编码。凭据(用户名和密码)以用户名—密码的格式编码。请注意,凭据是编码而不是加密的,因此它容易受到特定的安全攻击,因此实施基本认证的 REST API 不可避免地需要通过 SSL(https)进行通信。

我们还需要了解认证与授权的区别。认证验证谁(用户)正在访问 API,而授权则是访问 API 的用户是否有权限或被授权访问 API 资源。在我们投资者服务示例中,管理员用户被授权查看所有投资者,而个人用户没有被授权查看其他投资者的信息。

Spring 安全框架提供了一个开箱即用的安全实现,我们将使用 Spring 提供的功能来保护我们的 API。由于我们将在第九章中讨论更多的安全实现,深入探讨 RESTful 服务范式,因此在本章中我们将坚持使用基本认证实现。

使用默认密钥的授权

通过 Spring 安全框架,使用基本认证来保护 REST API 变得异常简单。只需在pom.xml中添加以下条目,就可以为我们的投资者服务应用提供基本认证:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

现在重新构建(mvn clean package)应用程序并重新启动。现在是时候使用 Postman 工具测试我们的 API 了。当我们点击 URL 时,与我们的早期示例不同,我们会看到一个错误,抱怨“访问此资源需要完整授权”:

截图

前面的错误是由于将 spring-security 添加到我们的pom.xml文件中。我们现在如何访问我们的 REST API 呢?别担心,在我们启动应用的控制台中,我们可以观察到使用默认安全密码的文本,或者在日志文件中搜索它。这是任何人访问我们 API 的密钥。以下截图显示了这一点:

截图

正如我们在前面的截图中所看到的,我们的控制台显示了我们可以用来访问我们 API 的密码,因此让我们使用它来访问我们之前遇到错误的相同 URL:

截图

正如我们在前面的截图中所观察到的,我们需要为我们要访问的 API 提供 BasicAuth 作为 Authorization 头;我们现在将看到没有认证错误的结果。请注意,携带XYZKL...令牌并以前缀Basic开头的 Authorization 头,因为我们使用 HTTP 认证头来强制 REST API 认证。

使用凭证进行授权

在许多实时场景中,我们需要使用特定的凭证来访问 API,而不是默认的凭证;在这种情况下,我们可以通过使用一些额外的现成 Spring 模块来增强我们的投资者服务应用,并使用我们自定义的凭证来保护它。

在我们的投资者服务中,我们将有一个新的类,称为PatronAuthConfig.java,它帮助应用强制对我们要保护的 URL 进行凭证验证:

@Configuration 
@EnableWebSecurity 
public class PatronsAuthConfig extends WebSecurityConfigurerAdapter { 
..... 

正如我们在前面的代码块中所看到的,通过几个注解,我们可以实现安全性。以下代码片段显示了覆盖方法实现以及一些代码行:

@Override 
protected void configure(AuthenticationManagerBuilder authMgrBldr) throws Exception { 
  authMgrBldr.inMemoryAuthentication()                      .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance())                  .withUser(DEFAULT_USER_ID).password(DEFAULT_USER_SECRET).authorities.... 
      } 

@Override 
protected void configure(HttpSecurity httpSec) throws Exception { 
httpSec.csrf()..... 
..... 

以下代码强制在每个请求中验证凭证的授权头:

httpSec.csrf().disable().authorizeRequests().and()
.authorizeRequests().antMatchers(NO_RESTRICT_WELCOME_URI)
.permitAll().antMatchers("/investors/admin")
.hasAuthority(DEFAULT_ADMIN_ROLE).antMatchers("/investors/invr*/**")
.access("hasAuthority('"+DEFAULT_USER_ROLE+"')").anyRequest()
.authenticated().and().httpBasic().and().logout();

请注意,我们的示例使用了以下用户名和密码,以及两个角色。我们提供了示例执行脚本和 Postman 脚本,只需点击标签即可执行各种场景:

用户 ID 密码 角色
admin admSecret ADMIN
user usrSecret USER

现在,请观察以下截图:

截图

前面的截图展示了一个 REST API 调用的示例运行,该调用需要管理员凭据作为认证头,并显示了有效凭据的结果。在左侧,我们可以看到各种测试用例;每个都有一个必要的先决条件。现在我们需要逐个运行它们并观察每个案例的结果。确保查看每个执行的认证头。

第三章中投资者服务示例与这个认证示例的唯一区别在于我们添加了一个新的类,PatronsAuthConfig.java,它扩展了使用授权头实现的认证配置。

统一合同

如我们之前在API 版本控制部分提到的,服务将始终随着额外的功能、增强和缺陷修复而演变,然而,现在服务消费者可以消费我们服务的最新版本,而无需更改他们的实现或 REST API 端点。此外,服务消费者需要了解这些服务合同的最新和演变细节。

统一合同模式可以解决这些问题。该模式建议以下措施:

  • 标准化服务合同并使其在所有服务端点上一致

  • 将服务端点从单个服务功能中抽象出来

  • 遵循 REST 原则,其中端点仅使用 HTTP 动词,并且仅使用 HTTP 动词表达底层资源的可执行操作

请参考API 版本控制部分中的 API 版本控制示例,因为实现已经可用,并且具有统一合同的味道(请参考我们的投资者服务示例,包括GETPOSTDELETE等操作)。

实体端点

如果服务客户端想要与实体(如投资者和他们的股票)交互,而不需要他们管理投资者和股票的复合标识符,我们需要一个名为实体端点的模式。实体端点建议将每个实体公开为它们所在服务的独立轻量级端点,以便服务消费者获得服务实体的全局可寻址性:

图片

前面的截图说明了服务消费者如何访问单个实体端点而不是服务端点。实体端点公开可重用的企业资源,因此服务消费者可以重用和共享实体资源。

我们的服务,投资者服务,公开了一些实体端点,例如/investors/investorIdinvestor/stockId,它们是我们服务消费者可以重用和标准化的实体端点的几个示例。

端点重定向

改变服务端点并不总是理想的,然而,如果需要,服务客户端会知道并使用新的端点吗?是的,通过标准的 HTTP 返回代码,3xx,以及通过位置头,通过接收301 永久移动307 临时重定向,服务客户端可以相应地行动。端点重定向模式建议返回标准 HTTP 头,并为过时的端点提供对当前端点的自动引用:

图片

如我们所见,服务消费者可以调用在位置头中找到的新端点。

如果你想尝试 3xx 代码和位置头,请参考我们的投资者服务示例中的头实现。

请注意,在服务中实现 HATEOAS 时,客户端可以避免这些端点重定向。

幂等

想象一下,银行的借记 API 在从客户账户扣除一定金额后立即失败。然而,客户端并不知道这一点(因为它没有从服务器收到任何响应)并重新发出借记调用!唉,客户端损失了钱。那么服务实现如何处理消息/数据并产生相同的结果,即使经过多次调用?

无效的字典意义是表示一个集合的元素,当它涉及某些操作或被自身操作时不会被改变

幂等是基本弹性和可扩展模式之一,因为它将分布式系统中的服务实现节点解耦。无论处理数据还是消息,服务都应该始终设计为坚持幂等性。

有一个简单的解决方案:使用 HTTP Web API 的幂等能力,其中服务可以保证由于与服务通信中断而导致的任何重复调用都是安全的,并且服务器可以无副作用地处理这些多个调用。

请参考第三章,基本 RESTful API 模式,以了解DELETEPUTPATCH的示例,因为这些都是幂等服务典型实现的例子;也就是说,即使我们对同一股票多次调用DELETE也是安全的;同样也适用于PUT

当涉及到处理并发时,服务可以通过 E-Tag 增强,并发送回一个409冲突响应来通知客户端所调用的资源处于不一致的状态。

批量操作

我们已经看到了许多 REST API 模式和它们的实现。然而,我们还没有讨论 REST API 中的一个基本模式,称为批量操作。我们的设计应该蓬勃发展,这将减少性能瓶颈,如响应时间,以及服务器和客户端之间的往返次数。

在我们的电子邮件客户端中将一封封电子邮件标记为已读可能是一个批量操作的例子;客户选择多封电子邮件标记为已读,然后通过一次 REST API 调用完成工作,而不是多次调用底层 API。

让我们来看看我们的投资者服务 API:如果客户端想要为他们的投资组合创建一组股票而不是一个接一个地创建,客户端需要根据他们想要创建的股票数量多次调用我们的 REST 端点。如果他们需要更新 100 个股票,他们需要调用端点 100 次,这确实不是一个优雅的解决方案。在这种情况下,批量操作模式可以提供帮助,而不会违背 REST 原则。让我们看看我们的投资者服务如何修改以提供批量操作。

投资者服务接受单个元素的PATCH操作,我们需要考虑增强相同的资源以接受多个插入,或者我们可以有另一个独立的 URI 来支持批量操作。为同一资源使用另一个 URI 不是一个干净的方法,因为它可能偏离了 RESTful 原则,因此让我们继续使用相同的PATCH请求来支持通过请求更新多个股票。以下两种方法被建议用于实现批量操作:

  • 基于内容的批量操作

  • 基于自定义头操作标识符的批量操作

我们的代码示例遵循两种方法;请注意,我们不会使用框架提供的注解的任何特定细节来执行批量操作。然而,我们继续使用自定义头和增强的请求体来支持客户端请求中的股票列表。以下截图显示了非批量操作和批量操作补丁请求以及头部的差异:

图片

以下是从InvestorController类中的代码片段,它强制执行自定义头,与第三章中的PUT示例(仅接受一个Stock对象)不同,Essential RESTful API Patterns(接受一个Stock对象):

@PatchMapping("/investors/{investorId}/stocks") 
  public ResponseEntity<Void> updateStockOfTheInvestorPortfolio(@PathVariable String investorId, @RequestHeader(value = "x-bulk-patch") Optional<Boolean> isBulkPatch,                  @RequestBody List<Stock> stocksTobeUpdated) throws CustomHeaderNotFoundException { 
            // without custom header we are not going to process this bulk operation 
            if (!isBulkPatch.isPresent()) { 
                  throw new CustomHeaderNotFoundException("x-bulk-patch not found in your headers"); 
            } 
      investorService.bulkUpdateOfStocksByInvestorId(investorId, stocksTobeUpdated); 
            return ResponseEntity.noContent().build(); 
      } 

通过运行我们的PATCH示例,我们可以通过在一个请求中分组多个项目来理解 RESTful API 的批量操作。请注意,批量操作可能涉及许多其他方面,例如 E-tag、异步执行或并行流实现以使其有效。然而,我们在这里不涉及这些特殊主题,并鼓励读者参考资源、参考文献和进一步阅读部分以获取资源。

批量操作与批量操作

批量操作处理单个请求中的单个目标操作,该操作涉及单个请求中的多个业务对象列表,而批量操作(本章未涉及)处理异构和同构的业务对象列表,但涉及多个请求。

电路断路器

我们经常遇到断路器;断路器是一种自动开关,旨在保护整个电路免受短路或过载引起的过电流负载损坏。

当服务与其他许多服务交互时,同样适用此概念。任何(网络)问题导致的故障都可能在整个应用程序中产生灾难性影响,防止级联影响是断路器模式的唯一目标。因此,此模式有助于子系统优雅地失败,并防止子系统故障导致整个系统完全失败:

图片

上一张截图说明了断路器概念,其中一个下游服务无法通信。然而,断路器实现以这种方式处理它,使得API继续为多个客户端提供服务。

在我们深入实施之前,让我们了解构成断路器的三个不同状态:

  • 关闭:这是指所有服务连接都完好无损(关闭),所有呼叫都通过预期服务进行。此状态需要跟踪故障以确定阈值限制。如果故障数量超过阈值限制,服务将移动到开启状态以避免级联影响。

  • 开启:服务的开启状态负责返回错误,而实际上并不执行其预期功能。

  • 半开启:一旦服务进入开启状态,它应该定期(超时)检查导致服务处于开启状态的故障。此外,如果这些故障仍然发生,它将继续保持服务处于开启状态,直到下一次检查。如果不再检测到故障,此状态的责任是触发回关闭状态以实现连续功能。

是时候动手实现断路器了。我们将使用 Spring 注解,以及一个名为hysterix的极其强大的开源库,我们可以用很少的努力实现整个概念。

我们需要至少有两个服务来解释断路器实现,因此我们正在创建断路器服务和断路器消费者服务,这些服务将实现断路器模式。以下部分阐述断路器的投资者服务实现。

启动我们已开发的任何服务,或者,为了简化,我们有一个名为断路器服务的轻量级服务,请注意,此服务没有针对断路器的特定实现。

现在是实施关键部分的时候了;即,一个名为断路器服务消费者的新服务,它将包含所有必要的断路器实现,以及对我们第一个服务(断路器服务)的调用。让我们执行以下步骤:

将 hysterix 依赖项添加到我们的 pom.xml (circuit-breaker-service-consumer/pom.xml):

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>

注释 InvestorController 以启用断路器并引入一个新的方法供我们测试 (circuit-breaker-service-consumer/com/**/InvestorController.java):

@EnableCircuitBreaker @RestController
public class InvestorController {
@Autowired
private InvestorService investorService;
// call the downstream service circuit-breaker-service@GetMapping(value="/welcome", produces="text/plain;charset=UTF-8")
public String welcomePageWhichProducesCharset() {
return investorService.circuitBreakerImplWelcome();
}

使用 Hystrix 命令并为断路器实现创建一个方法以及一个回退方法,如下面的代码块所示。我们将有两个方法:一个用于实际调用 (InvestorService.java/circuitBreakerImplWelcome),另一个用于回退 (InvestoreService.java/welcomeUrlFailureFallback),以便在实际调用出现故障时,应用程序将调用回退方法:

 @HystrixCommand(fallbackMethod="welcomeUrlFailureFallback") public String circuitBreakerImplWelcome() {
logger.info("reached circuit breaker consumer circuit breaker impl");
RestTemplate restTemplate = new RestTemplate();
URI circuitBreakerServiceURI = URI.create(CIRCUIT_BREAKER_SERVICE_URL);
return restTemplate.getForObject(circuitBreakerServiceURI, String.class);
}
// fall back method for welcome page failures
public
 String
 welcomeUrlFailureFallback
 (){ logger.info("lucky we have a fallback method");
return WELCOME_URI_FALLBACK_MESG;
}

太酷了,不是吗!使用 Hystrix 实现断路器就这么简单。现在,让我们按照以下步骤构建和运行我们的服务:

  1. 打开一个终端并构建 circuit-breaker-service (mvn clean package of circuit-breaker-service)

  2. 启动 circuit-breaker-service

  3. 打开一个新的终端并构建 circuit-breaker-service-consumer

  4. 启动 circuit-breaker-service-consumer

  5. 打开 Postman 并运行 http://localhost:9090/welcome

  6. 观察日语欢迎文本

  7. 现在,执行断路器实验

  8. 停止我们在步骤 2 中启动的 circuit-breaker-service

  9. 再次运行步骤 5,现在观察英文消息

是的,断路器回退已被激活,因此我们看到的是英文消息,而不是日语消息,因为 circuit-breaker-service 已经宕机。然而,/welcome URI 并未中断。

以下截图捕捉了验证断路器实现的各个阶段:

图片

以下截图是这一过程的另一个阶段:

图片

如前述截图所示,在断路器实现之前,/welcome 调用失败并显示控制台连接错误消息。然而,在实现断路器之后,相同的调用显示了回退方法的内容,尽管与 circuit-breaker-service 的连接仍然失败。

鼓励读者和设计师测试并增强 circuit-breaker-consumer,为其他方法调用添加更多的回退方法。

断路器模式的缺点是,涉及的应用程序/服务可能会经历轻微的性能下降。然而,这对于许多实际应用来说是一个很好的权衡。

结合断路器模式和重试模式

作为软件设计师,我们理解优雅地处理应用程序失败和故障操作的重要性。通过结合重试模式和断路器模式,我们可以获得更好的结果,因为它为应用程序处理故障提供了更大的灵活性。

重试模式使应用程序能够重试失败的操作,预期这些操作将变得可用并最终成功。然而,这可能导致我们的应用程序中出现拒绝服务(DoS)攻击。断路器模式防止应用程序执行可能失败的操作。那么,一个对断路器返回的任何失败都敏感的智能重试机制如何?这表明没有瞬态故障,因此应用程序放弃任何进一步的重试尝试。

API 门面

我们从《设计模式:可复用面向对象软件的基础》(GoF)中了解到名为门面的模式,该模式将复杂的子系统从调用者抽象出来,只向最终用户暴露必要的细节作为接口。API 门面也符合相同的定义和实现。

让我们看一下以下图表,它描述了客户端在有和没有 API 门面模式实现的情况下,对多个服务调用的简单实现:

图表

正如我们在前面的图表中看到的那样,客户端调用一个 API 门面以使其在客户端需要多个服务调用的情况下更简单、更有意义。然而,这可以通过单个 API 端点来实现,而不是客户端调用多个端点。API 门面提供了高可扩展性和高性能。

我们的投资服务为其删除操作实现了一个简单的 API 门面。正如我们之前看到的那样,删除方法调用设计意图方法。然而,我们通过引入一个简单的接口到我们的投资服务中,将设计意图方法抽象为调用者。这使门面出现在我们的 API 中。

删除服务的接口如下所示:

public interface DeleteServiceFacade {
 boolean deleteAStock(String investorId, String stockTobeDeletedSymbol);
    boolean deleteStocksInBulk(String investorId, List<String> stocksSymbolsList);
} 

删除服务接口的实现如下代码片段所示:

@Component
public class DeleteServiceFacadeImpl implements DeleteServiceFacade {
   private static final Logger logger = LoggerFactory.getLogger(InvestorService.class);
   private InvestorServicesFetchOperations investorServicesFetchOperations = new InvestorServicesFetchOperations();
       @Override
       public boolean deleteAStock(String investorId, String stockTobeDeletedSymbol) {
             boolean deletedStatus = false;
             Stock stockTobeDeleted = investorServicesFetchOperations.fetchSingleStockByInvestorIdAndStockSymbol(investorId,
                          stockTobeDeletedSymbol);
             if (stockTobeDeleted != null) {
                    Investor investor = investorServicesFetchOperations.fetchInvestorById(investorId);
                    deletedStatus = investor.getStocks().remove(stockTobeDeleted);
             }
             designForIntentCascadePortfolioDelete(investorId, deletedStatus);
             return deletedStatus;
      }
.....
.....

作为一项简单的练习,我们鼓励读者将我们的断路器服务调用实现为 API 门面,并在删除服务中完成批量删除方法。

前端后端

到目前为止,我们已经开发了各种 RESTful 服务 API 和端点,以便任何应用程序开发者都可以开始使用它们。然而,我们需要确保这些服务将满足各种类型的设备,因为它的真正目的是为使用任何设备的任何客户提供服务,而不仅仅是桌面用户或基于 Web 的应用程序。

在任何设备上提供更好的用户体验至关重要,无论其后台服务如何,如果我们不能为异构的最终用户及其设备提供更好的使用这些服务的机制,那么掌握所有可能的最佳实践开发 RESTful 服务也就变得毫无意义。

后端为前端BFF)是一种模式,最初由 Sam Newman 描述;它有助于弥合任何 API 设计差距。BFF 建议在用户体验与其调用的资源之间引入一个层。它还帮助 API 设计者避免为多个接口定制单个后端(服务):

图片

上述图表展示了 BFF 模式的一个简单实现,即针对桌面和移动设备都提供专属接口。每个接口都可以定义其必要的和独特的需求,以满足前端需求,而无需担心影响其他前端实现。

让我们考察每个接口的几个具体要求,以便我们了解为什么我们需要为相同的后端服务拥有多个接口:

  • 响应负载的格式和大小可能因每个客户端而异

  • 由于需要调用服务的次数较多,可能会出现性能瓶颈和优化需求

  • 当需要共享或通用后端服务,但开发和维护开销较少时

在某些情况下,如多个接口向后端发送相同的请求,或仅使用一个接口与后端服务交互时,BFF 可能不适用。

请注意,BFF 存在一些缺点,因此在决定使用独立的、专属的 API/接口时,请谨慎行事,因为它需要额外的、终身的维护,包括在各个层级上的安全改进、额外的定制设计,这可能导致安全漏洞和缺陷泄露。

摘要

通过几个代码示例学习模式总是能给我们带来深刻的见解。在本章中,我们讨论了 API 版本控制、使用授权保护 API,以及通过统一合同、实体端点和端点重定向实现来使服务客户端具备一致性。我们还学习了幂等及其重要性,它为 API 提供了批量操作的能力。本章最重要的部分是使用 Hysterix 实现的断路器,其中我们设想并实现了 API 设计的弹性模式。

在介绍了各种高级模式后,我们以 BFF 模式结束了本章,并学习了它是如何通过帮助开发团队为特定环境开发专属接口来减少跨实现团队中的 churns(不稳定)。

在下一章中,我们将介绍 RESTful API 网关,这应该对 API 设计者来说同样是一个令人兴奋的阅读材料。

进一步阅读

第五章:微服务 API 网关

由于微服务通常是细粒度的,任何大型应用都必须由许多微服务组成。在 IT 环境中拥有数百个微服务,IT 复杂性必然会增加。微服务的运营、监控、测量和管理复杂性肯定更大,因此,利用 API 网关的想法应运而生,并蓬勃发展,不仅是为了减轻不断上升的复杂性,而且是为了从微服务源代码中抽象出所有类型的通用能力。在本最佳实践章节中,我们解释了 API 网关在微服务时代的作用和责任。进一步来说,你还可以看到如何通过智能利用 API 网关基础设施来确保微服务应用的可靠性。

技术要求

本章主要讨论 API 网关解决方案对微服务成为企业级、关键任务、云托管、面向服务、事件驱动、创新丰富、流程感知、生产级和业务中心应用正确且有益成分的关键贡献。我们本章讨论了流行的 API 网关解决方案。我们还通过 API 网关实现了一个聚合服务。微服务(订单服务、客户服务和聚合服务)的源代码存放在本书的 GitHub 上。本书的 GitHub 链接是 github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices

关于微服务架构

传统应用缺乏灵活性,封闭,单体,庞大,还有更多。引入业务和技术变更会遇到许多问题和风险。第三方服务集成是一项艰巨的任务。为传统系统增加如 Web、移动和云等额外接口也是另一项困难。如今,出现了多种接入渠道,我们的应用需要具备与多个渠道协同工作的内在能力。在未来,企业将从他们的 IT 团队和合作伙伴那里要求更多,而付出更少。简而言之,传统应用的开发和运营复杂性过大,难以承受。微服务架构MSA)旨在实现规模化的速度和安全,MSA 生态系统持续增长,提供大量技术、工具和框架,以高效实施各种业务应用和 IT 服务。这种方法将 IT 提升为可行的增值业务合作伙伴。也就是说,以业务为中心的 IT 因 IT 领域的众多显著进步而成为新常态。之前持有的观点(IT 以成本为中心)正在迅速改变,IT 正自豪地宣布为以利润为中心的范式。

基于微服务架构的任何软件应用都仅仅是由一系列小型、模块化且易于管理的服务组成。每个服务都在一个独特的进程中运行,并通过定义良好的 API 进行通信。RESTful 接口是服务之间寻找、绑定和使用彼此最流行且轻量级的入口。API 驱动的微服务能够与业务无缝对接,以敏捷的方式应对变化,与业务变化匹配敏捷响应,并以去中心化的方式提供解决方案。微服务可以独立部署,水平扩展,可组合,互操作,公开可发现,网络可访问,技术无关,模块化(松散耦合且高度内聚),细粒度,等等。容器化和微服务的结合为企业和技术专业人士带来了巨大的节省和好处。各种微服务正在有条不紊地容器化,并在裸金属BM)服务器和虚拟机VMs)上托管。容器轻量级的特性以及以业务为中心和特定目的的微服务为商业世界带来了许多关键优势。还有其他技术进步,如容器编排和管理解决方案,以形成容器/服务集群,实现多容器、业务感知、任务关键、自适应和复合应用。

随着微服务的兴起并成为设计、开发、部署和管理生产级商业应用程序和 IT 平台的简单且最优构建块,现有的单体应用程序正在被细致地分割成多个互动和有洞察力的微服务。经过验证的“分而治之”技术现在仍然表现良好,甚至通过 MSA 难题也是如此。这些独立但相互关联的微服务可以分别推进和部署,而不会影响其他微服务。因此,在商业、技术和用户方面都有许多优势,因此商业高管、技术专业人士和 IT 运营商都热衷于采用 MSA。新的应用程序正在使用 MSA 的显著特性从头开始构建。在接下来的章节中,我们将看到 API 网关基础设施对于实现微服务架构预期成功的重要性。

我们在本书的其他章节中,通过大量的实际例子,讨论了 RESTful 服务范式广泛的成功。其核心在于,RESTful API 允许消费者/用户应用通过适当选择链接(资源),例如/product/book/,以及通过特定的 HTTP 操作(方法),例如GETDELETEPOSTPATCH,来在应用程序(网页、云、移动、商业、物联网等)中前进。这导致下一个资源,实际上代表了应用程序的下一个状态。REST 代表表示性状态转移。并且这种新状态被传输给消费者,以便其后续使用。

微服务为中心的应用程序中的突出基础设施模块

水平扩展和独立部署被吹捧为微服务的关键特征。微服务和它们的实例可以轻松复制、替换、重构和恢复,以适应传入的流量。新的应用程序正在被设计、开发和部署为微服务,而遗留应用程序正在被系统地分割成一组协同工作的微服务。因此,很明显,MSA(微服务架构)是下一代软件应用的主要架构模式和风格。在本节中,我们将讨论 MSA 合规系统的关键基础设施组件。

服务注册

微服务正变得越来越动态和广泛分布。因此,为了利用微服务并实现服务发现(静态和自动),我们需要一个集中的服务注册/存储库机制。服务注册旨在跟踪已注册的微服务实例,以便提供关于服务和它们最新位置的正确信息。这个注册表相当于一个数据库,用于准确包含和维护服务实例的网络位置。如果发生移动,那么微服务必须主动接近并更新服务注册表。API 网关在接收到客户端微服务的请求后,会连接并尝试获取服务微服务的位置详情。如果存在任何偏差或不足,那么可能会出现不希望的失败。每个微服务实例在启动时都必须向集中式服务注册表进行注册,并在关闭时进行注销。服务实例的注册通常通过心跳机制定期刷新。服务注册模块必须高度可用,为此,最推荐的方法是拥有服务注册模块的集群。不建议在 API 网关或注册感知客户端缓存从注册表中获得的网络位置详情。如果在 API 网关缓存位置详情,那么可能会降低 API 网关的性能。微服务实例更新注册表关于其变化状态(可用性)的责任是唯一的。服务注册的作用在以下图中说明:

图片

微服务、服务注册和 API 网关的交互

服务发现

我们解释了当确定微服务和其实例的位置细节时,服务注册解决方案如何派上用场。在单体应用程序中,查找和调用应用程序组件是通过语言级别的一种方法完成的。如果应用程序组件在不同的进程空间中运行,那么有远程方法调用(RMIs)、远程过程调用(RPCs)、分布式组件对象模型(DCOM)等。在传统的遗留 IT 环境中,应用程序在固定的和已知的地点运行。也就是说,主机和端口是固定的,以便被发现和使用。因此,应用程序可以轻松地使用任何一种标准通信和数据传输协议相互调用。然而,在敏捷且擅长的微服务时代,微服务和其实例的数量经常变化。进一步来说,为了实现期望的优化,微服务和其实例正在其他位置重新部署。因此,客户端微服务需要使用高级服务发现机制来了解微服务实例的最新状态。为了简化,有两种主要的服务发现模式——客户端发现和服务器端发现。

对于客户端发现,客户端将从服务注册表中获取服务实例的位置,因为注册表包含了所有服务最新位置的信息。客户端知道服务注册表的地址。然后,客户端使用负载均衡算法选择最佳服务实例并发出请求。

对于服务器端发现,客户端API 网关发起请求。然后,API 网关查询服务注册表以获取所需微服务的网络位置。基于该位置信息,API 网关尝试连接并利用为微服务提供服务的能力。

以下图表说明了客户端和服务器端服务发现的区别:

图片

组合/编排

微服务主要实现业务功能。为了创建复合型、以业务为中心且具有流程感知的应用程序,微服务必须相互连接。为确保组合,有两种方法——编排和协奏。此外,还有静态和动态的组合。API 网关可以充当编排模块。另一种选择是编写编排逻辑并将其作为独立的实用服务。以下图表清晰地描述了服务编排如何构建复合服务,这些服务具有流程感知性和业务中心性:

图片

转换

有几种不同类型的,例如物联网设备上的客户端应用程序、资源受限设备的浏览器、Web 和移动应用程序等。随着输入/输出设备生态系统的持续增长,任何应用程序和服务的客户端都必须单独处理。异构设备上的客户端应用程序遵循不同的数据表示、交换和持久化格式。此外,它们遵循不同的数据传输协议。然后还有同步和异步通信协议。在异构环境中,数据格式和协议转换的需求必然会增加。API 网关可以非常方便地促进转换和其他转换需求。可以根据新兴需求新鲜烘焙的转换适配器,并存储在集中位置以便随时找到和使用。对于无特定目的和特定设备,最优的数据格式和协议正在不断开发;需要新的数据和服务协议转换器,以便它们能够加入主流计算。

监控

监控系统资源和微服务对于实现 MSA 的原始效益变得至关重要。有服务和应用程序监控工具。由于大多数微服务正在容器化,容器监控工具最近获得了突出地位。甚至还有针对 Kubernetes 的监控工具,它已经成为容器生命周期管理平台的顶尖选择。鉴于本章专门准备来解释关于 API 网关的一切,讨论 API 网关监控是合乎逻辑的。API 网关对于微服务成功提供各种功能至关重要。每个服务请求/响应都会通过它们路由。API 的操作、性能、健康状况、可伸缩性、安全性和日志数据的监控和测量对于满足约定的 SLA 和 OLA 参数至关重要。强烈要求的健康监控是为了确保网关始终处于运行状态。监控对于确保服务可靠性和稳定性是必不可少的。可观察性方面对于服务的可用性、适应性和分析至关重要。为了精确监控系统的健康状况,处理能力、内存容量和 I/O 速率(网络和存储)等资源参数是主要关注点。其他需要精确监控和测量的重要事项包括连接性(网络)、安全警报、安全备份、恢复(数据和灾难)等。最后,日志数据在为相关方提供大量有用和可用的信息,以巩固和塑造业务连续性的独特目标方面发挥着非常重要的作用。

另一个重要的因素就是流量监控。收集和深入分析流量数据将使运维团队能够及时考虑、思考并实施正确的措施,具有清晰和自信。必须忠实考虑的基本指标包括在一定时间内发送给 API 的总请求数量、性能/吞吐量值、接收到的成功和异常消息数量、API 网关阻止的消息数量等。此外,还会考虑请求分类。这种深入而巧妙的分析有助于更准确地预测流量情况,以便在没有任何故障或减速的情况下处理任何类型的峰值和激增。

负载均衡和扩展

这是确保软件系统扩展可用性的重要因素。通过利用负载均衡器LB)(软件或硬件)来实现应用可扩展性(水平扩展)的目标。我们需要流量信息来及时预测和制定正确的对策。对流量数据的更深入实时分析有助于我们理解和估计应用的负载。从负载数据中获得的见解帮助云管理员和运维团队共同简洁地制定可行的应用可扩展性政策和规则。可以及时准备额外的基础设施模块,以便承担额外的负载。API 网关也可以进行水平扩展和垂直扩展,以确保 API 网关解决方案的高可用性。否则,API 网关可能会成为单点故障。为了有一个集群化的 API 网关设置,我们可以在 API 网关前面放置一个 LB。这意味着可以利用多个 API 网关解决方案的实例来确保连续性,所有这些实例都可以运行相同的配置;这种一致性有助于虚拟化相同的 API 并执行相同的策略。如果有多个 API 网关组,那么能力(负载均衡)可以优雅地扩展并准确地在组间完成。

API 网关不对负载均衡器(LBs)提出任何额外要求。也就是说,用户和数据负载是基于广泛推荐的特征进行平衡的,包括响应时间、当时系统的负载等。API 网关以无状态的方式维护,以确保它们不会被状态信息拖累。这也使得服务消息可以采取任何路线到达适当的和指定的服务。一些突出的组件,如缓存和计数器,通常存储在分布式缓存中,会针对每条独特的消息进行细致的更新。这种设置最终有助于 API 网关在所有模式下(粘性和非粘性)无任何问题地完成其义务。

API 网关的分布式特性在主动/主动和主动/被动集群中带来了一定的限制。例如,为了丢失任何计数器和缓存状态,系统必须设计成在任何时候至少有一个 API 网关处于活动状态。精确地说,为了确保高可用性和可靠性,正如之前所指出的,必须以连接和集群的方式运行多个 API 网关实例。API 网关能够通过稳定和连续的配置部署来保持零停机时间。通常,集群中的 API 网关实例需要几秒钟来更新其配置。在更新时,该特定实例不会处理任何新的请求。然而,集群中的其他 API 网关实例可以不断地接收和处理新的请求,并交付结果。负载均衡器(Load Balancer)在这里的关键作用是确保所有入站请求都推送到接收和处理新请求的正确 API 网关实例。因此,API 网关集群对于持续接收和响应服务消息非常重要,负载均衡器在满足这一需求中扮演着至关重要的角色,如下面的图示所示:

图片

高可用性和故障转移

在微服务时代,通过容错、故障检测和隔离来保证高可用性对于架构师来说是一件重要的事情。在最近的一段时间里,API 网关解决方案作为微经济时代的一个关键组件应运而生。微服务架构被誉为促进强制业务适应性、流程优化和自动化的灵魂和救星。API 网关是微服务之间寻找和交流的唯一入口,以完成业务任务。这种代理/中间件解决方案拥有许多常用功能,以便微服务只需关注业务功能。非功能性需求(NFRs)和服务质量(QoS)属性是通过网关解决方案实现的。为了实现高可用性和稳定性,推荐的做法是将 API 网关以高可用性(HA)模式部署。正如之前所述,API 网关实例通常部署在标准负载均衡器(LB)之后。LB 持续探测 API 网关实例,以了解它们是否处于活动状态。每个实例的健康状况和性能水平被 LB 捕获并用于及时采取适当的补救措施,以确保系统连续性不受任何影响。如果 LB 得知某个 API 网关无法正常工作,那么 LB 将重定向和路由入站流量到正在运行并履行其义务的网关实例。

配置了及时警报,以便在发生意外事件时获得相关通知。如果触发警报,则可以理解问题及其元数据,因为数据分析能力是任何 API 网关产品的重要功能。通常,API 网关被保持为无状态实体,因为它们被设计和注定要每秒处理数百万个服务请求消息。然而,API 网关可以维护缓存数据,这些数据可以在 API 网关集群之间复制。这种安排有助于维护 API 网关实例之间的对等关系。

高可用性和故障转移指南

专家们制定了一系列指南和最佳实践,以确保系统的可用性高:

  • 为了保证最大可用性,API 网关必须使用经过验证的主动/主动模式。

  • 对流量数据进行更深入和决定性的分析是必要的。这种分析得出的见解帮助操作员和其他维护生产环境的人员进行规划和防范消息洪水。

  • 工具支持的自动化网络基础设施监控和管理对于确保最高可用性至关重要。不仅收集操作和日志数据,而且对这些数据进行各种调查可以揭示大量有用和可用的信息。因此发现和传播的所有知识对于使网络基础设施在最佳和原始状态下工作大有裨益。API 网关解决方案内嵌的分析功能在分析和阐述防止任何类型故障和失误的措施方面非常方便。还有特定的以及通用的监控工具,可以与知识可视化/报告生成工具集成。

治理

随着 API 数量的持续增加,建立政策和实施其他机制以进行有效的监控和管理变得至关重要。这些政策可以大致分为设计时和运行时治理。政策受到业务目标和目标的高度影响。日益增长的 IT 升级是为了满足业务情绪的变化。因此,IT 政策必须与业务期望同步,以产生坚实和智能的治理。

关于 API 网关解决方案

简而言之,API 网关是一个多功能的代理,完成各种集成、中介和丰富任务。它拥有关于主要微服务端点的一切信息,以便正确和认知地中介、路由和调用相应的端点。这是在初始请求验证、内容过滤、身份验证和授权之后进行的。

典型的 API 网关必须具备以下固有的和可服务的能力。任何 API 网关解决方案的常见功能包括身份验证和授权、消息增强、修复、基于流程的组成、流量路由和管理以及服务监控。

API 网关注定要为一个或多个内部 API 提供一个单一且统一的 API 入口点。可能会有不同的分布式来源,例如客户端应用、服务和设备,试图访问 API 网关。客户端发送各种请求并期望得到适当的响应。网关应该执行各种启动、中介和实施任务。其中之一就是统一这些请求并与后端服务协同工作。所有类型的代理和聚合活动都是通过 API 网关完成的,同时确保满足速率限制和安全需求。在微服务时代,可能会有数百个服务,因此 API 网关和管理平台的需求必然进一步增长。简而言之,API 网关可以帮助为外部消费者提供一个统一的入口点。编排/舞蹈、经纪、发现、路由、增强、策略执行、治理、礼宾服务等都是由标准化的 API 网关解决方案执行的。另一方面,API 管理增加了额外的功能,如分析和生命周期管理。在未来,将会有尝试通过复制和容错,结合 API 网关、集群和编排平台、服务网格解决方案等,来满足 QoS 和 NFRs(如可用性、可伸缩性、高性能/吞吐量、安全性和可靠性)。

针对以微服务为中心的应用的 API 网关

随着时间的推移,API 网关在以有益方式操作微服务方面的独特贡献正在增长。API 网关的主要特点如下:

  1. 增加灵活性:API 网关应该隐藏内部关注点,对外部客户端不可见。API 网关解耦外部 API 和内部微服务 API。这种抽象简化了在现有微服务实现中添加、替换、替换和替代高级微服务的过程。内部微服务的 API 可以更改,而不会影响请求的微服务。服务可以新鲜注册并在服务注册表或存储库中引用。新服务的服务发现可以平稳且无错误。服务可以进行版本控制。

  2. 增加了一个额外的层:由于微服务不是直接接触,因此服务安全性得到了极大加强。通过使用 API 网关,可以阻止对内部微服务的各种恶意攻击。每个微服务都有自己的数据存储。因此,不仅服务安全性,而且数据安全性也得到了保障。通过 API 网关提供的速率限制/节流,可以轻松阻止分布式 DoS 攻击。

  3. 支持数据与协议转换:存在多种不同的数据传输和通信协议。有同步和异步通信及其相应的协议。除了 HTTP 之外,还有其他协议,如 ProtoBuf (developers.google.com/protocol-buffers/docs/reference/overview)、AMQP (www.amqp.org)、COAP 等,用于与第三方应用程序和服务集成,以产生和维持集成应用程序。这是 API 网关解决方案的本质。确切地说,所有微服务的常见功能都被安全地抽象并集成到 API 网关中。这种最佳实践使得微服务简单且易于操作。所有安全、网络、第三方集成、增加容量、满足 QoS 属性和其他横切关注点都累积在一起,并通过 API 网关实现,API 网关充当集中式和集群式中间件解决方案。

以下图表说明了 API 网关解决方案如何将微服务与服务客户端连接起来:

从本质上讲,API 网关是微服务的反向代理,并为不断增长的客户端列表提供进入系统的单一接触点。这为微服务时代实现了古老的门面模式。进一步来说,API 网关解决方案简化并标准化了 API 的设计、实现和管理。确保了微服务、它们的实例和软件基础设施的一致性。正因为这种一致性,建立和实施微服务的服务级别和安全要求变得非常容易。在系统堆栈的不同层次和级别上制定和巩固策略对于微服务架构的成功至关重要。网关最终有助于解决诸如安全、缓存、监控等关键挑战和关注点。它可以处理异构客户端、多个后端微服务和它们的实例。服务发现通过 API 网关实现自动化。以下图表显示了 API 网关解决方案的各种功能和特性:

微服务 API 网关的问题

我们已经列出了 API 网关对以微服务为中心的业务应用的几个关键优势和贡献。然而,也有一些缺点。众所周知,这个网关基础设施是一个额外的抽象层,因此所有的控制和数据流都通过这个中间件解决方案进行;因此,存在系统性能下降的可能性。这引入了一个额外的中心,服务请求和响应通过它传递。这不仅是一个接触点,也是一个故障点。当微服务的数量显著增加时,复杂性将稳步增加。API 网关不提供服务到服务的通信弹性。有服务网格解决方案,可以保证所需的服务弹性,从而实现可靠的应用。随着技术先进的云基础设施的广泛应用,我们可以安全地预期会有许多可靠的系统和环境。

API 网关中的策略配置——在前一节中,我们指出 API 网关能够执行内容攻击保护CAP)。通过指定和修改安全策略,API 网关可以阻止任何安全攻击。内容攻击主要是通过将恶意数据插入服务请求消息来执行的。最知名的内容攻击包括插入特殊字符。其他突出的内容攻击方法包括文本模式、SQL 和 XPATH 注入。克服此类攻击的方法是为入站和出站流量配置适当的 CAP 策略。这些措施可以防止 SQL 和 XPATH 注入攻击。其他考虑因素包括通过限制 HTTP 版本、方法和 URL 路径来禁止安全攻击。还有其他方法,例如定义域名白名单、客户端 IP 地址、限制查询参数和 HTTP 头。

物联网设备(客户端)通过 API 网关向微服务发送消息请求。入站 CAP 策略会扫描服务请求消息,查找任何可能的内容攻击。如果有任何违规,API 网关将错误消息发送回物联网设备客户端。如果一切正常,API 网关将验证并验证后的消息传递到服务中介层进行身份验证和授权。然后,调用正确的微服务端点并处理消息。微服务随后调用一个或多个微服务。然后,出站 CAP 策略扫描回复消息,查找任何内容攻击。如果没有违规,则将响应发送到请求的客户端。

API 网关的安全特性

在任何分布式 IT 环境中,安全都扮演着至关重要的角色。数据完整性、机密性和可用性是确保数据安全不可渗透的最重要参数。有几种机制,如加密和解密、数字签名、哈希等,用于在数据传输过程中保护数据,持久性和使用。对于运行在云基础设施上的以微服务为中心的应用程序,安全方面始于识别、认证和授权。安全策略也是公共云环境中广泛使用的解决方案。硬件安全模块HSMs)如今很普遍,因为它难以被入侵,同时保证了更高的吞吐量。然后还有防火墙、入侵检测和预防系统等几种安全设备。考虑到微服务时代安全威胁和漏洞的严重性,统一威胁建模和管理解决方案也受到了很多关注。如前所述,API 网关对于微服务架构的成功至关重要。考虑到战略上的重要意义,API 网关解决方案正被填充以增强安全性。许多独特的安全特性被纳入 API 网关,以确保最高和不可破的安全。让我们来看看这些安全特性。

联合身份认证是服务认证和授权广泛采用的方式。众所周知,微服务专注于业务功能。当需要时,支持功能和设施才会被附加。分而治之的成熟技术在 IT 世界中仍然发挥着神奇的作用,因为 IT 空间中持续演变的复杂和精密趋势和转变使得 IT 世界变得极其复杂。特别是,作为首要安全需求的身份管理,正被委托给第三方解决方案和服务。也就是说,每个微服务不需要在后续请求中验证用户凭据时获取和存储它们。

相反,身份管理系统负责良好的认证。以下图表显示了身份验证和授权过程中授权服务器所扮演的角色。附着的数据库以集群模式存储所有用户凭据。此外,第三方认证和授权管理系统与 API 网关紧密耦合,以便无缝和顺利地完成初始安全增强任务:

图片

有三个关键协议使得联合身份认证成为可能:

  • OpenID

  • SAML

  • OAuth

流程如下。应用程序客户端首先发送请求,并通过提供必要的凭证从第三方认证和授权服务器获取JWT访问令牌。一旦获得必要的凭证,客户端然后将访问令牌嵌入 API 请求的 Authorization HTTP 头中。API 网关随后验证客户端提供的访问令牌是否与授权服务器匹配。一旦第三方认证服务器验证通过,API 网关将JWT访问令牌传递给适当的后端微服务以启动业务任务。如果需要其他下游微服务来满足服务请求,则相同的JWT令牌将在所有参与和贡献的微服务之间共享。处于协作模式的微服务必须将其请求消息附加JWT访问令牌:

  • 保密性:由于远程存储,数据安全和隐私需求很高。此外,无处不在的公共和开放互联网,最终成为世界上最大的通信基础设施和信息强国,是数据载体。确保数据安全是商业实体及其 IT 部门的首要要求。也就是说,数据的保密性不能以任何代价被破坏。主要的数据保护由 API 网关完成。另一种选择是数据库服务器完全与其他服务器隔离。为了确保数据在持久化过程中的隐私,数据被加密,加密密钥被单独管理。不允许客户端直接访问数据服务器。每个数据访问请求都通过前端服务路由。

  • 诚信:包含机密、客户和公司信息的消息服务不能被黑客攻击和操纵。被破坏的消息可能被用于错误的目的,例如关闭服务器或窃取私人信息。为了确保消息和数据有更好的诚信度,在消息从源头到目的地的过程中通过众多中间服务器传递时,会进行一系列特定目的和不可知性的检查。通常使用散列算法来识别是否发生了任何数据修改。

  • 可用性:在微服务架构中取得成功,服务可用性非常重要。有黑客试图关闭服务。API 网关提供了第一道防线。然后,还有负载均衡器来确保服务连续性。集群和云服务器在保证服务高可用性方面非常有用。通过应用节流/速率限制模式可以阻止对服务的分布式拒绝服务(DDoS)攻击。

  • 安全通信:通信必须通过 SSL/TLS 机制进行加密。因此,微服务和 API 网关必须符合 SSL/TLS 标准。这种设置可以轻松地防止中间人攻击。此外,广泛使用的消息和数据加密方法可以防止窥视和篡改服务消息和数据。

除了其他功能外,API 网关主要用于确保基于微服务的应用程序的安全性。由于微服务部署在地理上分布的服务器环境中,因此安全性具有特殊的重要性。此外,随着 Web 规模应用程序的发展,微服务和它们的独立实例正由负载均衡器(LBs)作为前端。API 网关代表了一个不断增长的先进服务集合,这些服务使微服务能够为业务自动化和增强做出贡献。

显著的 API 网关解决方案

有几种合格的 API 网关解决方案(开源和商业级)。

Kong (konghq.com/) 是一个开源的 API 网关解决方案。Kong 服务器可以运行在任何 RESTful API 之前。Kong 完全支持流行的 REST API。由于其轻量级和多功能性,RESTful 已成为各种 Web、移动、嵌入式和云应用程序广泛使用的 API 标准。Kong API 网关的当前功能可以通过插件显著扩展,这些插件引入了额外的功能以满足不断变化的需求。也就是说,Kong 提供的核心功能和设施可以通过提供多功能插件的附加功能得到补充。也就是说,Kong 原生支持即插即用架构。全球的小型、中型和大型企业都在生产级 IT 环境中使用这个创新领域的产品套件。Kong 可以部署在按需、在线和远程云中。此外,它还可以在本地私有云中运行。如图所示,OpenResty 和 Nginx 是 Kong 的核心引擎。OpenResty 是一个高性能的 Web 平台,它集成了标准的Nginx核心、LuaJIT和一系列库。Kong 使用 Cassandra 或 PostgreSQL 作为数据存储。Kong 强调的流行功能包括认证、监控和分析,以及请求/响应转换和日志记录。

为了更好地理解 Kong 的工作原理,以下是一个使用 Kong 的典型 API 请求工作流程:

图片

一旦 Kong 开始运行,对API的每一个请求都将首先击中 Kong 服务器。Kong 服务器充当代理,将请求发送到所需服务的API。简而言之,微服务世界中的所有操作和实现都是通过 API 网关来完成的。当所有操作都使用API时,API 网关、管理和分析解决方案的作用在未来的日子里必将升级:

图片

红帽 3scale APIcast 网关 (www.3scale.net/tag/open-source/)——APIcast 网关也是基于 Nginx 的,作为一个开源软件解决方案提供。这个 API 网关配置在 3scale 的 Admin Portal 中。网关是 3scale API 管理的重要组成部分,这是一个软件即服务SaaS)的提供。Admin Portal 具有许多定制和配置功能,例如允许客户定义所需的认证方法、设置速率限制、对 API 使用数据进行分析,并为他们的 API 消费者创建开发者门户。3scale APIcast 网关允许您通过几点击部署 API 网关服务。这种部署比其他方式快得多,因为不需要在后台进行任何代码修改。APIcast 正在成为低或中等流量 API 的完美解决方案。企业广泛在预发布环境中使用 APIcast,这加快了测试过程。

Tyk (tyk.io/) 也是一个开源的 API 网关。这个解决方案本质上负责 API 管理活动。Tyk 包包括一个 API 网关和一个 API 管理仪表板。它还具备 API 分析功能。开发者门户是这个包的另一个有趣模块。Tyk 可以安装在本地云环境中。它可在领先的公有云中提供,并且可以作为云服务购买和使用。不仅如此,它还整合了私有和公有云,为混合云提供 API 网关解决方案。在负载下,这个 API 网关可以无障碍地执行完整的密钥验证、安全验证、配额管理和数据分析。开发者门户的提供旨在赋能开发社区。

Moesif (www.moesif.com/)主要是一个 API 分析解决方案。它有能力理解开发者如何使用 API,并知道为什么某些错误发生并有时重复。此外,它还帮助在客户看到之前通知 API 提供商任何隐藏的问题。因此,这个解决方案是服务提供商的帮手。随着我们走向 API 经济,API 分析解决方案扮演着重要的角色。今天,数千名开发者通过 Moesif 处理数十亿次的 API 调用,用于调试、监控和理解 API 使用。构建优秀的 API(无论是 REST、GraphQL 还是 JSON-RPC API)的首要要求是精确简洁地衡量开发者如何使用 API。产品团队使用该解决方案提供的 API 分析能力来了解他们的 API 是如何被使用的。通过利用经过验证和潜在的机器学习ML)技术,Moesif API 洞察使数据驱动型团队能够持续改进他们的 API 和开发者体验DX)。

Ambassador (www.getambassador.io/)是一个流行的开源和 Kubernetes 原生 API 网关,适用于微服务世界。这个网关解决方案可以为容器世界做很多事情。Ambassador 可以在智能地将请求路由到后端服务之前对各种传入请求进行身份验证。它原生支持 TLS 终止。除此之外,Ambassador 通过外部第三方服务支持速率限制/节流。这种速率限制是通过 Envoy 代理的速率限制功能实现的。Envoy 的一个关键特性是其可观察性功能,该功能通过公开其自身操作的大量统计数据来启用。Ambassador 使得将知识传播到统计和监控工具(如 Prometheus、Datadog 等)变得容易。Ambassador 通常依赖于快速发展的 Kubernetes 平台,以确保服务的可靠性、可用性和可伸缩性。

Envoy (www.envoyproxy.io/)最初由 Lyft 构建。Envoy 是一个高性能的 C++分布式代理,专为单一服务和应用程序设计。Envoy 最终证明是一个作为通信总线的合格解决方案。对于以微服务为中心的应用程序,这贡献了通用的数据平面。最后,它是服务网格解决方案的核心引擎。Envoy 与每个应用程序并行运行,并通过以平台无关的方式提供所有常见网络功能来抽象网络复杂性。通过利用 Envoy 网格解决方案,可以可视化并定位问题区域,因为诸如一致的可见性、调整整体性能和添加基础结构等特性都可在单一位置提供。

树形网关 (treegateway.org/) 是一个免费且开源的解决方案。它可以创建一个完整且可定制的管道来处理服务请求。树形网关使得创建和维护大型集群变得容易。除此之外,它还支持快速形成 Redis 集群以共享配置、断路器状态和缓存内容。任何 API 配置都可以在任何时候更改,并且所有配置都会无问题地传播到其他树形网关集群节点。这还附带了一个高级的断路器模块,当任何 API 失败、崩溃和犹豫不决时,它可以快速失败。此外,它天生支持实时监控和分析。一个可插拔的引擎允许对任何 API 请求进行任何类型的转换或验证。

Gravitee.io (gravitee.io/) 是一个灵活、轻量级、速度极快且开源的 API 管理解决方案。这个工具帮助任何企业以细粒度的方式控制用户如何、何时以及访问 API。

API 伞 (apiumbrella.io/) 是一个位于任何 API 前面的代理解决方案。它可以无缝地添加 API 网关和分析功能,例如 API 密钥、速率限制等。

Express 网关 (www.express-gateway.io/) 是一个基于 Express.js 的微服务 API 网关,它通常运行速度快、灵活,并由社区驱动。

API 网关解决方案对于 API 驱动的微服务非常重要。RESTful API 是最普遍和强大的。RESTful API 被广泛应用于 Web、移动、云和物联网应用程序和服务中。不仅软件应用程序,而且资源受限和密集型嵌入式设备也通过 RESTful API 相互连接。随着微服务的快速普及和 API 的广泛应用,需要高级 API 网关解决方案来形成集成和协调的应用程序,而企业现在越来越倾向于这些解决方案。API 管理和分析能力也附加到网关解决方案中,使其更加全面。各种插件和实用服务正在根据需求构建和集成。此外,API 网关解决方案正在与第三方工具集成,用于监控、测量、管理和可视化。理想的方法是拥有一个动态的模块化(松散耦合且高度内聚)API 网关服务池,而不是一个难以管理、缺乏灵活性和封闭的单体 API 网关解决方案。

服务网格与 API 网关

我们已经广泛讨论了与 MSA 范式成功相关的 API 网关解决方案。我们都知道,为了提高微服务的弹性,服务网格解决方案正受到宠爱。在本节中,我们将讨论 API 网关和服务网格之间的关键区别。

首先,正如前文所述,API 网关的关键目标是向外界表达和暴露微服务。通过附加 API 管理模块,API 得到良好的管理。API 数据被捕获并接受各种调查,以产生洞察力,引导 API 网关走向正确的方向:

  • API 服务调用下游的微服务,这些微服务可以是原子性的和组合性的。API 网关的一个显著能力是将多个下游服务融合成对请求服务有用的东西。也就是说,服务根据既定要求进行混合,以产生过程感知、任务关键和组合服务。

  • API 网关还内置了对服务发现、分析和安全性的支持。捕获各种指标的可观察性能力,以及监控、分布式日志记录和分布式跟踪,是网关解决方案的关键区别。

  • API 网关与多个其他软件解决方案紧密合作,例如 API 管理、市场/商店和门户,以便在微服务时代实现全面性。

其次,服务网格——这是一种相对较新的解决方案类型和方法,用于提供弹性特性,这对于基于微服务的应用程序变得越来越重要。众所周知,服务应该相互交互,以实现更大更好的业务规模服务。当服务相互交谈时,可能会出现一些问题。为了确保服务通信的弹性,IT 行业正倾向于接受服务网格的新概念,这是一种确保服务弹性的网络和通信基础设施。服务网格实现内置了弹性启用模式,如断路器、重试、超时和节流/速率限制。有如 Istio、Linkered 和 Conduit 等服务网格解决方案。高级功能,如服务发现和可观察性,正在被纳入这些解决方案中。API 网关和服务网格解决方案的功能有明确的界限。在生产环境中,也可以同时使用它们。

服务网格通常与其他服务实现一起作为边车使用。服务网格为启用服务弹性提供了一系列实用和横向功能。

总结来说,API 网关促进了客户端应用程序和服务器应用程序之间的 API 通信。此外,应用程序内的微服务可以通过 API 网关解决方案进行集成。在第七层(HTTP)运行的 API 网关能够实现内部和外部通信。其他值得注意的服务包括用户身份验证、节流/速率限制、转换、日志记录等。

如 Istio、Linkered 和 Conduit 之类的服务网格解决方案旨在启用服务通信的弹性。也就是说,它们主要关注内部通信的路由。服务网格主要在第四层(TCP)运行。所有弹性可靠性设计模式,如断路器、超时、重试和健康检查,都是内在实现并集成到服务网格解决方案中的。

摘要

微服务正被宣称为一种开创性的架构风格,用于生产和维护商业和 IT 应用程序和平台。云环境中充满了裸金属服务器、虚拟机和容器。微服务可以托管在这些环境中并运行以提取和提供它们独特的功能。随着微服务数量的快速增长,我们需要技术支持的复杂度缓解解决方案和服务。API 网关解决方案被提出作为可行的和值得尊敬的基础设施(软件或硬件)解决方案,它提供了一种抽象,以减少依赖性引起的问题。本章详细介绍了 API 网关解决方案的各种功能以及它们如何有助于解决各种特定于微服务的问题。为了您的利益,记录了关键的网关解决方案(开源和商业级)及其独特的属性。下一章将介绍微服务必须以系统化的方式进行测试,并通过众多自动化测试工具加快测试需求。

第六章:RESTful 服务 API 测试和安全

声称企业就绪的任何软件都必须经过严格的测试周期,获得质量保证认证,并满足多个质量标准,以便在生产服务器上使用。软件应用程序的安全性是另一个决定其是否会在生产服务器上使用的关键方面。

为了让读者了解生产和企业级 RESTful API 的不同方面,本章将讨论这些质量和安全措施的各种方面。

本章的目的是带读者进行 API 测试之旅。以下是我们将涵盖的里程碑或主题:

  • API 测试的类型

  • API 测试的挑战

  • API 测试中的安全性

  • 此外,我们还将向您展示各种 API 测试工具、API 安全工具和框架的概览

在 API 测试之旅的中途,读者将进行一次小插曲,介绍一些安全问题和 API 漏洞,并学习如何在 API 测试中揭露它们。

软件测试概述

任何产品,无论是简单的安全别针还是宏伟的飞机,都需要经过一个过程来确保它解决了其创造的目的,软件也是如此。软件测试是通过验证和验证其目的,从端到端来确认软件的准确性和质量的过程。

因此,任何软件产品或应用的主要重点是验证(根据文档要求检查一致性和对齐)和验证(检查系统的准确性,并验证最终用户的需求与实际结果之间的差异)。

让我们列出一些软件测试的基本成果,然后转向下一部分,重点关注 API 测试:

  • 断言并确保实际与需求期望之间没有差异

  • 断言并确保软件产品无论用户数量多少都能持续可用

  • 预测和揭示隐藏的问题

  • 断言并确保产品与预期的最终用户平台、浏览器等无缝运行

RESTful API 和测试

由于本书涉及 RESTful API,本章旨在引导读者了解一些基本最佳实践和 API 测试原则,以及一些测试框架。通过集成测试(包括手动和自动的),我们可以完成分布式应用的大部分关键 API 测试策略,并帮助这些应用达到生产就绪、可部署,并成为 CI/CD 的一部分,同时确保每个版本的可扩展性和稳定性。

以下部分介绍了 API 测试的基础、API 测试方法、它们的类型等。

API 测试的基础

我们在前面章节讨论的软件应用程序产品,具有各种软件层,例如用户界面(UI)、业务逻辑层、中间件和数据库。API 测试和认证主要关注业务层上的数据集成测试。API 测试是一种软件测试,涉及直接的API 测试,与其它通用测试不同,后者主要涉及用户界面:

图片

上述图示展示了软件的典型层,其中API 测试位于业务层,而功能或 UI 测试位于表示层

理解 API 测试方法

在开始 API 开发时商定 API 测试的方法是一个关键的 API 策略。让我们看看 API 测试的一些原则:

  • 明确定义范围并充分理解 API 的功能

  • 常见的测试方法,如边界分析和等价类,是 API 测试用例的一部分

  • 制定计划,定义输入参数,准备零和样本数据以供 API 使用

  • 确定并比较预期和实际结果,并确保没有差异

API 测试类型

在本节中,我们将回顾 API 测试的各种类别,并继续探讨最佳实践。

单元测试

涉及验证单个操作的测试是单元测试。在我们的投资者服务 API 示例中,第四章,“高级 RESTful API 模式”,我们涵盖了大量的单元测试用例,以下是一个特定单元测试用例的样本代码片段,用于验证从 API 获取所有投资者:

    @Test 
    public void fetchAllInvestors() throws Exception{ 
          RequestBuilder requestBuilder =  
               MockMvcRequestBuilders.get( 
                      "/investors").accept( 
                      MediaType.APPLICATION_JSON); 
          MvcResult result =  
              mockMvc.perform(requestBuilder).andReturn(); 
          MockHttpServletResponse response = 
              result.getResponse();      
    } 

API 验证测试

所有软件都需要快速评估,并确认其创建的目的。验证测试需要在开发过程的最后对每个开发的功能进行运行。与关注 API 的特定部分或功能的单元测试不同,验证测试是一个更高层次的考虑,通过回答一系列问题,以便开发可以进入下一阶段。

验证测试的一组问题可能如下:

  1. 一个产品特定的问题,例如,是否是请求的必要功能?

  2. 一个行为问题,例如,开发的功能是否做了预期的事情?

  3. 一个与效率相关的问题,例如,预期的功能是否以独立和优化的方式使用必要的代码?

所有这些问题,本质上都是为了验证 API 符合约定的验收标准,并确保其完美地遵循关于实现预期最终目标和满足用户需求和要求的规范。

功能测试

涉及 API 特定功能和它们的代码库的测试是功能测试。通过 API 验证活跃用户数量、回归测试和测试用例执行都属于功能测试。我们之前在第三章,基本 RESTful API 模式和第四章,高级 RESTful API 模式中看到了许多使用 Postman 工具执行的功能测试的例子。以下截图可能会刷新你对投资者服务用户认证验证此类功能测试例子的记忆:

UI 或端到端测试

包括 GUI 功能和 API 功能在内的涉及和断言端到端场景的测试,在大多数情况下,验证应用程序的每个事务,被归类为端到端测试。

压力测试

如我们所知,用户数量的增加不应影响应用程序功能的表现。压力测试将揭示这些问题,并验证 API 在正常条件下的性能。

运行时错误检测测试

帮助监控应用程序并检测诸如竞争条件、异常和资源泄露等问题属于运行时错误测试类别。以下要点简要介绍了这些因素。

监控 API

测试各种实现错误、处理程序失败和其他 API 代码库内部的固有关注点,确保它没有可能导致应用程序不安全的漏洞。

执行错误

对 API 的有效请求返回响应,并断言它们为预期的有效响应是常见的,然而,断言无效请求以期望失败也是 API 测试策略的一部分,这些测试属于执行错误:

前一个截图展示了第三章,基本 RESTful API 模式的例子,当用户提供的 ID 在系统中不存在时,预期会出现错误。

资源泄露

通过向 API 提交无效请求来验证底层 API 资源故障的负面测试。在这种情况下,资源包括内存、数据、不安全性、超时操作等。

错误检测

检测网络通信故障。由于提供错误的凭据而导致的认证失败是一个错误检测场景的例子。这些测试确保错误被捕获并得到解决:

我们在第四章,高级 RESTful API 模式,投资者服务示例中遇到了认证错误,如图所示的前一个截图描绘了这一点,因为代码返回了401(正如预期的那样);这是一个错误检测测试的例子。

REST API 安全漏洞

API 因其简单、结构化、开发速度快和部署迅速而受到欢迎和广泛使用。这自然带来了确保实现免受各种威胁的挑战,例如中间人攻击MITM)、缺乏 XML 加密、不安全的端点和 API URL 参数。

REST API 与 Web 应用程序具有类似的安全漏洞;我们将在以下章节中介绍最常见的 API 攻击和漏洞,然后转向安全测试。

暴露敏感数据

测试 REST API(或任何应用程序)的首要和最重要的安全方面是评估和确定数据在传输或持久化状态下的数据类别和数据保护需求。例如,个人信息、信用卡信息、健康记录、财务信息、商业信息以及许多其他此类信息类别都需要保护。

此外,数据通过加密得到根本性的保护,因为没有加密的敏感数据泄露可能导致攻击者窃取敏感数据,这不仅仅限于个人数据、信用卡号码、身份盗窃等等。

根据数据的分类(和数据保护需求),你可以应用许多预防措施和保护手段来保护敏感数据。以下是一些保护措施的列表:

  • 根据数据分类进行数据分类并应用控制措施。

  • 除非必要,否则不要存储敏感信息,并尽快丢弃。使用令牌化和截断方法来防止敏感数据泄露。

  • 加密是一种必要且至关重要的保护措施。

  • 不要为敏感数据实现缓存(或禁用敏感数据交易的缓存)。

  • 为密码使用盐和自适应(具有可配置的迭代次数)散列方法。

理解认证和认证攻击

认证是一个确定实体(一个过程、一台机器或一个人类用户)身份的过程,以允许或拒绝该实体访问底层应用程序功能。

登录认证、网络认证、IP 认证、远程认证、基本认证和客户端证书是一些认证类型。

认证攻击是黑客尝试利用认证过程并获取未授权访问的过程。成功渗透认证系统并获得未授权访问将允许黑客窃取敏感信息并更改、破坏或删除有价值的数据。想象一下黑客假扮成某人的身份;这是身份盗窃,可能导致个人损害和金钱盗窃。更糟糕的是,如果被黑客攻击的身份是网络或服务器管理员,那么损害可能超乎我们的想象。

认证类型攻击包括绕过攻击、暴力攻击(针对密码)、验证冒充和反射攻击。

当我们在讨论认证攻击时,回顾一个基本认证的示例实现第四章,高级 RESTful API 模式可能是个好主意。投资者服务有一些必要的安全措施,如基本认证、使用默认密钥的授权以及使用凭证进行授权,以限制 API 访问仅限于真正的用户。我们还讨论了如何使用 Postman 测试和验证这些安全措施。

以下截图描述了一个这样的场景(使用默认密钥的授权)以及我们如何使用默认安全密钥验证访问:

图片

理解授权和 OAuth2 方案

如我们所知,证明一个正确的身份是认证,允许经过认证的用户执行特定操作是授权。在本节中,我们将简要介绍 OAuth——一个行业标准的授权协议——以及一些授权方案。当我们在本节中提到 OAuth IETF OAuth 工作组(tools.ietf.org/wg/oauth/)时,指的是 OAuth 2.0,它侧重于客户端开发者的简便性(一个 RESTful 原则),同时为各种应用用例提供特定的授权流程,例如桌面应用程序、Web 应用程序、移动电话,甚至物联网(IoT)支持的客厅设备。

在我们继续介绍 OAuth 2.0 方案之前,让我们通过以下列表了解为什么你会选择基于 OAuth 的授权而不是传统的基于 Cookie 的授权:

  • 基于 Cookie 的授权主要是状态性的,也就是说,服务器必须记录活跃用户会话。为了管理活跃会话,服务器需要多次数据库调用以维护状态。此外,一些其他服务器端开销使得将授权过程从应用程序服务器(以无状态的方式)解耦变得困难。

  • 基于 Cookie 的认证和授权涉及域名,因为应用程序可能与多个域名交互,因此底层服务器需要一些额外的配置,从而导致维护和安全开销。

  • 集成第三方客户端,如 Google+和 Facebook。在许多情况下,使用基于 Cookie 的授权对应用程序进行授权不是一个可行的解决方案。

  • 基于 Cookie 的授权被认为是一个维护噩梦(在某些情况下,如原生移动应用),因此不是许多应用程序的首选选择,尤其是在依赖基于移动的认证时。

OAuth 通过允许任意客户端(例如,第一方 iOS 应用程序或第三方 Web 应用程序)通过授权服务器以安全、可靠和有效的方法访问资源服务器上的用户(资源所有者)的资源来解决这些担忧:

上述图表描述了 OAuth 授权的利益相关者和他们的角色。

现在,让我们看看一些基于 OAuth 2.0 的授权方案以及你可能会选择这些特定方案的情况或业务案例,以下表格展示了这些内容:

方案/流程 客户端类型 简要描述
隐式 单页应用(如 Google Fonts) 应用请求网关的访问令牌,用户授予权限
客户端凭据 机器到机器的非交互式程序,如服务、守护进程等 应用将客户端凭据传递给网关服务器并获取访问令牌
授权码 不太可信的应用(第三方应用请求访问你的应用) 应用发送从网关接收到的临时授权码,并由同一网关进行验证
资源所有者密码凭证 高度可信的应用(第一方应用) 客户端将请求用户授权凭据(通常是用户名和密码),然后客户端发送一些参数(grant_typeclient_idclient_secret)到授权服务器

作为本章的一部分,我们提供了实现资源所有者密码凭证流的 OAuth 2.0 示例代码,该代码可在 GitHub 上供任何人下载和执行。

以下图表描述了资源所有者密码凭证 OAuth 方案的标准流程,在我们进入解释如何运行该示例代码的部分之前:

如果你想在你的系统上运行和测试那个示例代码,以下步骤和一些 Postman 工具的截图将会很有用:

  1. 从 GitHub 下载代码(你可能需要参考第三章的技术要求部分 Chapter 3,Essential RESTful API Patterns)。

  2. 打开终端并使用cd命令进入downloaded_loc/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices\Chapter06\oauth2-sample

  3. 运行mvn clean install并等待BUILD SUCCESS消息。

  4. 使用java -jar target\ oauth2-sample-0.0.1-SNAPSHOT.jar命令运行应用程序。

  5. 打开本地 Postman 工具并测试 URL(在 Postman 集合的Chapter06文件夹中)。

  6. 当你运行不同的场景时,你会看到以下截图。

  7. 尝试错误条件和其它场景(例如提供错误的凭据,使用用户凭据而不是管理员凭据等)。

  8. 使用java -jar命令运行示例:

  1. 等待本地服务器启动并准备好接受请求:

  1. 打开 Postman 工具,运行Chapter06文件夹中的集合,并观察客户端凭据(对于授权服务器是必需的):

  1. 现在输入授权服务器的用户凭据以提供必要的令牌:

  1. 从授权服务器获取访问令牌,以便我们可以在下一步中使用它来访问必要的资源:

  1. 使用我们在上一步中获得的访问令牌访问管理员(资源)API。注意响应体:

  1. 使用必要的用户凭据对用户资源重复相同的步骤(注意输入中的用户名):

因此,你可以使用示例代码执行不同的场景(本章节未提供),例如错误消息和不同的 HTTP 错误代码(403302等)。

跨站脚本

跨站脚本XSS)攻击是将恶意代码作为输入注入到网络服务的过程,通常通过浏览器进行。一旦注入,恶意脚本可以访问浏览器保留的任何 cookies、会话令牌和敏感信息,甚至可以伪装为渲染页面的内容。XSS 可以分为服务器端 XSS 和客户端 XSS。

传统上,XSS 是三种类型之一:

  • 反射型 XSS

  • 存储型 XSS

  • DOM XSS

关于 WordPress REST API 缺陷中存储型 XSS 攻击的精彩阅读,请在此处找到:threatpost.com/wordpress-rest-api-bug-could-be-used-in-stored-xss-attacks/124294/ 以及在此处:threatpost.com/wordpress-silently-fixed-privilege-escalation-vulnerability-in-4-72-update/123533/

反射型 XSS

成功的反射型跨站脚本攻击发生在应用程序允许攻击者在一个 HTTP 响应中注入浏览器可执行代码(如 JavaScript、Applets、Action Scripts、Flash)时。你可能知道,注入的代码是非持久的,并且只有在用户打开恶意构建的链接或 URL 或渲染受影响响应的第三方网页时才会影响用户。

存储型 XSS

存储型 XSS,也称为持久型 XSS,通常被认为是有害且风险高,发生在恶意脚本作为输入注入到易受攻击的应用程序中,并在稍后由其他用户或管理员查看。

DOM XSS

第三种类型(由 Amit Klein 开发,自 2005 年以来可用),DOM XSS,发生在客户端代码使用不安全的引用到不由服务器提供的页面完全控制的 DOM 对象时。通常,但不限于,动态向页面注入攻击者可控制数据的 API 和 JavaScript 框架。单页应用程序容易受到 DOM XSS 攻击。

XSS 防护需要从用户输入中过滤恶意内容,并且还需要进行编码(转义)。

跨站请求伪造

跨站请求伪造CSRF),也称为 Sea Surf 或 XSRF,是一种一键攻击漏洞,Web 应用程序暴露了最终用户可能被强制(通过伪造链接、电子邮件和 HTML 页面)在当前已认证会话中执行不受欢迎的操作的可能性。

同步令牌模式、Cookie 到 Header 令牌、双重提交 Cookie 和客户端保护是常见的 CSRF 预防方法。

拒绝服务攻击

拒绝服务DoS)攻击的目的是通过发送大量虚假请求,使目标机器快速达到其最大负载(处理请求的能力),从而使目标系统拒绝进一步的真实请求。

洪水攻击和缓冲区溢出攻击是两种拒绝服务(DoS)攻击类型。在洪水攻击中,攻击者通过向服务器生成大量流量来饱和目标服务器,导致目标服务器最终陷入 DoS 状态。

另一方面,缓冲区溢出攻击的目的是针对一台机器,并使该机器消耗所有可用内存或硬盘空间,或者导致 CPU 使用率过高。这会导致各种后果,如系统响应缓慢或行为迟钝,甚至可能导致目标系统崩溃,造成潜在的灾难性后果。

请注意,通常,DoS 攻击发生在恶意用户(攻击者)具有比目标服务器更多可用带宽的网络中。Smurf 攻击、ping 洪水和 ping of death 攻击是一些实际的 DoS 攻击。

分布式拒绝服务

分布式系统上的 DoS 攻击被称为分布式拒绝服务DDoS)攻击。DDoS 攻击通过使用多个被入侵的计算机系统作为攻击流量源,包括网络资源如物联网设备,来实现成功。

注入攻击

最有害和最危险的攻击之一是注入攻击。攻击者向应用程序提供不受信任的输入,该输入作为命令或查询的一部分被执行/处理,从而导致应用程序行为的部分或完全失控,并导致诸如数据盗窃、数据丢失、数据完整性丢失和 DoS 等后果。它甚至可能导致整个系统被完全控制。

下表总结了几个常见的注入攻击类型、每种类型的简要描述及其潜在影响:

注入类型 简要描述 潜在影响
代码注入/操作系统命令注入 使用应用程序代码执行操作系统命令 利用更高权限的漏洞获得更高权限,并可能导致整个系统被完全控制
CRLF 注入 在输入序列中注入 EOL/回车字符 导致 HTTP 头部分割,便于在响应体中注入任意内容,包括 XSS
电子邮件(邮件命令/SMTP)注入 向邮件服务器注入 IMAP/SMTP 语句 个人信息泄露和垃圾邮件中继
主机头注入 通过根据用户输入动态生成头信息滥用 HTTP Host 头信任 缓存投毒——操纵缓存系统并服务恶意页面密码重置投毒——利用密码重置电子邮件并直接向目标发送恶意内容
LDAP 注入 注入并执行轻量级目录访问协议LDAP)语句 修改 LDAP 树的内容并授予非法权限、权限提升和绕过身份验证
SQL 注入 注入伪造的 SQL 命令以执行数据库读取或修改数据 导致数据丢失、数据盗窃、数据完整性丢失、DoS,甚至由于 SQL 注入的高级变体而导致完全系统妥协
XPath 注入 通过向应用程序注入虚假数据执行伪造的 XPath 查询 导致信息泄露和绕过身份验证

不安全的直接对象引用

不安全的直接对象引用IDOR)与其他顶级 API 漏洞一样有害;它们发生在应用程序根据用户输入(如 ID 和文件名)暴露对内部对象的直接访问时。

让我们通过以下图例快速了解 IDOR。在图中,鲍勃从应用程序中获取 ID 为 1001 的文件是有意义的,但他是如何获取 ID 为1003的爱丽丝的文档的呢?

正在发生直接对象引用,因为开发者暴露了对内部实现对象的引用——在这里,在先前的示例中,它是一个文件(对象引用可能是一个目录、图像文件、数据库键等)——与应用程序。

因此,没有验证机制,允许鲍勃(攻击者)操纵这些引用以访问未经授权的数据被称为 IDOR 漏洞。

可以通过绘制所有用户输入作为直接引用(如图例所示)并用作引用对象的端点来测试 IDOR 漏洞。始终建议有两个或更多用户来覆盖直接对象和函数。

缺少函数级访问控制

IDOR 的另一个方面是缺少功能级访问权限。应用程序可能遗漏了实现功能级访问权限,因此任何具有网络访问权限的人都可以发送请求并获得响应,而不仅仅是具有特权的特定用户。例如,没有管理员级别访问权限的用户不应能够访问管理员 URL。

应用程序中敏感请求处理程序缺乏保护的 API 属于缺少功能级访问权限漏洞的范畴,因此允许黑客在没有必要授权的情况下渗透应用程序。

测试这种漏洞的各个方面应侧重于两个基本场景——用户是否可以直接浏览资源,以及访问 API 资源的 UI 是否向该 UI 暴露了未经授权的资源。

中间人攻击

中间人攻击(MITM attack)是指攻击者将自己置于网络或用户与应用服务器之间的通信中间。他们的意图是窃取、监听、伪装、秘密中继、拦截或篡改两个通信方之间的通信,包括 API 消息,而这一切都表现得像正常的信息交换正在进行中:

图片

上述图示了一个典型的中间人攻击,其中窃听者模仿并转发通信/响应给呼叫者,就像它们来自服务器一样,它们将看起来是真实的。

中间人攻击的一个例子可能是发出会话令牌的 API 与充当用户浏览器和 HTTP 头(会话令牌)之间中间人的攻击者之间的通信。因此,拦截那个会话令牌很容易,因为它打开了访问用户账户的通道,然后根据该账户的权限进行破坏。

常见的中间人攻击类型和防护措施

在以下列表中,有一些常见的中间人攻击类型,你需要意识到,还有一些针对这些攻击的防护措施:

  • 嗅探:嗅探,也称为数据包嗅探,其中攻击者使用广泛/免费的数据包捕获工具,通过特定的无线设备检查和监控通过网络通信的数据包。

  • 数据包注入:以这种方式将恶意数据包注入数据通信流中,使其与有效数据流混合,并看起来像是原始/预期通信的一部分。

  • SSL 剥离:在运行中将 HTTPS 网络通信更改为 HTTP,使通信不安全,这是另一种中间人攻击形式(用户甚至可能没有意识到他们被重定向到不安全的端点),并且以明文形式泄露敏感信息,攻击者可以迅速获取这些信息。

  • 电子邮件劫持:这是一种非常常见的中间人攻击类型,攻击者模仿一个受信任的网站(例如,银行的网站)向目标账户发送带有指示的电子邮件,并说服账户持有人遵循电子邮件中的指示,从而导致灾难性的后果,如失去金钱/个人信息等。

  • Wi-Fi 间谍活动:这涉及到设置专用的 Wi-Fi 访问点来诱使用户连接并让他们使用该网络。一旦用户连接到这些 Wi-Fi 访问点,攻击者将拦截并获取他们的凭据、信用卡信息以及更多敏感信息。

  • 会话劫持:一旦用户使用他们的凭据登录到应用程序,应用程序将生成一个临时令牌,这样用户就不需要再次提供凭据来访问后续页面。然而,攻击者可以嗅探并捕获那个会话令牌,并使用它来获取对用户账户的访问权限。

  • 防护措施:防止中间人攻击的防护措施如下:

    • 安全/多用途互联网邮件扩展S/MIME

    • 基于公钥基础设施PKI)的认证证书

    • SSL/TLS 证书

    • 系统和服务器配置

    • HTTP 严格传输安全HSTS

重放攻击和欺骗

重放攻击,也称为回放攻击,是一种网络攻击,其中攻击者(欺骗了有效交易)恶意多次重复(多次)有效的数据传输(本应只发生一次)。当服务器期望一个有效的交易时,它不会对请求是否为有效交易有任何怀疑。然而,这些是伪装的请求,对客户端产生灾难性的影响:

图片

上一张图展示了重放攻击的示例,其中合法用户发送了一个有效的请求,但攻击者欺骗了它并重新发送/重放给 API。

由于 RESTful API 无状态,这些 API 被重放攻击的风险很高(它们是易受攻击的目标)。因此,很明显,API 设计师/开发者需要在他们的 API 中为所有重放攻击采取对策。防护措施包括带有会话标识符的一次性密码、生存时间TTL)措施、客户端上的 MAC 实现,以及在请求中包含时间戳,以及像 Kerberos 协议预防、安全路由和挑战-握手认证协议CHAP)这样的安全协议。

漏洞原因

如前一小节所述,我们已经查看了一些漏洞,现在让我们也熟悉一下以下段落中导致 API 易受各种攻击的一些常见担忧和问题。

API 设计和开发缺陷

缺少或不遵守 API 安全原则和最佳实践可能导致暴露业务关键数据的缺陷。设计和开发的一个方面是尽可能简化 API,因为复杂性可能导致覆盖范围减少和漏洞。较差的用户输入验证、SQL 注入漏洞和缓冲区溢出是其他一些原因。

第二章,设计策略、指南和最佳实践,讨论了设计策略和 RESTful API 设计实践的各个方面。理解和实施这些设计原则和实践有助于减少设计和开发缺陷。

系统配置不良

即使设计和发展得再好,如果系统配置(API 所在位置)不遵守安全合规性,那么系统也可能无法得到充分保护。这也会导致漏洞,攻击者会窃取信息。

人类错误

不遵守组织安全合规性以及缺乏对安全措施的了解,例如文件销毁政策、安全编码实践、强密码、维护密码的机密性、定期重置密码以及防止访问未知/不安全的网站,这些都会在 API 中造成漏洞,可能导致安全漏洞。

内部和外部连接性

由于 API 是未受保护的内网和外网的一部分,因此未受保护网络中 API 的连接性是造成漏洞的另一个主要原因。此外,API 暴露于大型和独特的渠道,如移动网络、风险管理不善以及网络内的宽松授权实践等,都是这一类别漏洞的例子。

安全测试

安全测试确保 API 免受外部威胁,并保护我们之前章节中讨论的漏洞。API 安全测试和安全测试人员的主要重点是通过对 API 进行渗透测试、模糊测试、验证、敏感数据泄露确定等操作,找出他们打算测试的 API 的漏洞。

安全功能测试和安全漏洞测试是安全测试的两个类别。功能测试执行手动测试,并手动检查 API 实现中是否存在安全机制。安全漏洞测试执行可能暴露漏洞的自动化测试用例。

测试人员的最终目标应该是通过研究错误信息来理解系统行为,并暴露任何安全漏洞,例如未经授权的访问、IDOR、中间人攻击和重放攻击。

您可以通过运行渗透测试和模糊测试以及各种手动测试来实现安全测试目标。

本节详细讨论渗透测试和模糊测试,并讨论提供现成安全测试支持的工具/框架,以便 API 测试人员可以利用工具为底层 API 获得安全保障。

渗透测试或 pen tests

API 测试中的一个强制性要求是渗透测试,也称为 pen tests。pen tests 是通过模拟对系统或 API 的网络安全攻击来暴露/确定可利用的漏洞的过程,例如内部网络漏洞、XSS 攻击、SQL 注入和代码注入攻击。

突破测试从外部角度评估威胁向量,例如支持的功能、可用的资源以及 API 的内部组件。

让我们更详细地讨论突破测试的各个阶段、测试方法、支持突破测试的框架,以及选择最佳渗透工具的一些标准。

突破测试的重要性

在我们深入细节之前,以下理由将帮助我们理解为什么突破测试在 API 测试中如此关键:

  • 不损害数据隐私

  • 确保网络上的金融交易和金融数据的安全

  • 发现 API 和底层系统中的安全漏洞和漏洞

  • 模拟、预测、理解和评估攻击的影响

  • 使 API 完全符合信息安全规范

突破测试生命周期

现在我们已经从前面的部分很好地理解了漏洞的原因,让我们在本节中看看突破测试的五个阶段:

图片

上述图表描述了突破测试的生命周期,包括准备、扫描获取访问权限维护访问权限和报告等五个活动阶段。

让我们更详细地探讨以下各阶段。

准备、计划和侦察

生命周期第一阶段包括以下两个部分:

  • 范围、范围定义、确定要进行的测试目标,以及确定要处理的测试方法和系统

  • 收集有关域和端点等情报,了解目标 API 的工作方式,以及其暴露于漏洞的程度

扫描

通过静态和动态分析了解目标应用程序对各种入侵尝试的反应是扫描阶段的重点。

获取访问权限

这涉及尝试通过诸如 XSS、SQL 注入、代码注入和后门等应用程序攻击来揭露 API 漏洞。一旦发现这些漏洞,利用它们进行权限提升、数据窃取方法和流量拦截是获取访问权限范围的一部分,以及评估 API 漏洞可能造成的损害。

维护访问权限

网络中非法、长期的入侵者存在可能会对系统造成不可逆转的损害,因为他们可能在系统中存在很长时间,这有助于在持续、经过充分研究和精心策划的攻击中进行高度敏感的数据挖掘(尤其是在政府、军事和金融网络上)。

评估长期存在的能力,以及他们获得对系统/API 的深入访问的机会是维护访问权限阶段的主要目标。

分析

生命周期的最后阶段专注于编译和呈现渗透测试结果作为报告。这些报告通常包含作为渗透测试一部分被利用的特定漏洞,作为渗透测试练习的一部分被损害/访问的敏感数据细节,以及,最重要的是,你在系统中未被检测到的持续时间。这些结果和报告将作为组织内部安全配置的输入/输出,以防止任何未来的攻击。

API 测试的渗透测试类型

我们讨论了渗透测试在安全测试中的重要性,API 也不例外;它们都需要经过这些渗透测试,确保底层 API 没有暴露任何漏洞。请注意,在实际操作中有三种渗透测试类别,它们是——黑盒渗透测试、灰盒渗透测试和白盒渗透测试。

黑盒测试和灰盒测试假设测试人员对底层 API 的了解仅限于有限的范围。在本节中,我们将简要介绍白盒测试,因为它对于 API 安全测试至关重要,以及为什么在下一节中它被优先用于 API 渗透测试。

白盒渗透测试

白盒测试也称为结构测试开箱测试透明盒测试玻璃盒测试。白盒渗透测试是一种全面的测试方法,因为你在开始测试之前就能获得有关模式、源代码、模型等方面的全部信息。白盒测试旨在审查代码并捕捉任何设计和开发错误。它们是内部安全攻击的模拟。

API 渗透测试依赖于以下原因进行白盒测试:

  • 测试将在模块的所有独立路径上运行

  • 测试确认和验证代码内部的所有逻辑决策(true/false

  • 测试执行语法检查,因此发现对查找代码注入和 SQL 注入攻击至关重要的打印错误

  • 测试发现由程序逻辑流程与实际执行不匹配(针对意图的设计)引起的设计错误

有许多开源工具和商业版本可供扫描代码、检查恶意代码、使用数据加密技术查找安全漏洞,甚至找到硬编码的用户名和密码。以下表格中列出了一些工具(包括商业和开源版本):

工具 类型 提供商
Nmap OpenSSL Pure Hacking
Nessus Cain and Abel Torrid Networks
Metasploit THC Hydra SecPoint
Wireshark w3af Veracode

让我们通过指出 API 的渗透测试应该在真实攻击者发现之前暴露 API 漏洞,并继续进行模糊测试来总结本节。

模糊测试

模糊测试,也称为模糊化,是质量保证阶段最广泛使用的测试实践之一。它涉及向目标系统输入大量随机数据(噪声或模糊),目的是针对 API 以展示缓冲区溢出或其他不受欢迎的行为,甚至引发系统崩溃。

威斯康星大学的 Barton Miller 在 1988 年(作为他操作系统实用程序可靠性——模糊生成器项目的一部分)引入了模糊测试,以揭示 API、软件、网络和操作系统中的任何安全漏洞和编码错误。

API 模糊测试的主要目的并不是测试 API 的正确功能,而是通过模糊数据来探索和测试未定义的区域:

图片

如前图所示,模糊测试的范围是暴露底层 API 的任何意外行为。

模糊测试的生命周期

API 模糊测试的典型生命周期中的各个阶段从确定目标 API 和定义测试输入开始,以生成显示 API 中检测到的漏洞的日志结束。以下图示描绘了模糊测试生命周期的每个阶段:

图片

模糊测试策略

模糊测试策略根据攻击向量、模糊测试目标、模糊测试方法等因素而有所不同。对于一个 API,让我们专注于模糊测试目标。模糊测试主要有两种分类——基于变异的和基于生成的。我们将在下一节中查看这两种分类的详细信息。

基于变异的模糊测试

变异或愚昧的模糊测试是一种简单的方法,其中我们通过改变现有数据样本来创建新的测试数据。样本数据生成从协议的有效样本开始,并改变提供的输入,直到改变输入的每一个位。以下图示说明了两种变异的方法:

  1. 位翻转,其中输入以序列或随机方式翻转:

图片

  1. 在输入的末尾附加一个随机字符串:

图片

基于生成的模糊测试

基于生成的模糊测试,也称为智能模糊测试,是基于对已知格式、已知协议的理解,并按照系统/API 规范(RFC)和格式(例如,API 文档中的格式)从头生成输入的测试。

生成模糊测试能够根据数据模型构建测试数据。有时,这些测试只是注入随机字节,有时它们可以更加智能,知道良好的数据值并将它们以多种有趣的方式组合在一起(例如,将正则表达式作为请求体的一部分,在标题中使用主机名,以及将预期的响应类型更改为不同类型)。

在我们总结关于模糊测试策略的这一节之前,让我们了解另一种非常成功的模糊测试方法,即基于协议的模糊测试(也称为语法测试、语法测试和鲁棒测试)。在基于协议的模糊测试中,测试人员对协议格式有详细的了解,他们的理解取决于给定的规范。在这里,规范被视为一个存储在基于模型的测试工具中的规范数组,并且它还能够生成测试数据。然后,工具需要遍历所有规范,并在序列、数据内容等方面添加不规则性,以暴露漏洞。

请注意,由于变异方法不需要您了解协议,因此您可能会觉得与基于生成的方法相比,一开始使用起来更舒适。然而,基于生成的方法是一个彻底、更好且推荐的过程,尽管它需要更多时间,因为它涉及几个有效的输入组合。然而,它确实导致了更好的代码覆盖率和代码路径。

模糊测试的优点和缺点

以下要点旨在说明为什么模糊测试在软件专业人士社区中很受欢迎:

  • 由于它们没有关于系统行为的先入之见,因此它们极其简单、易于使用、成本效益高,并且快速设置

  • 它们的单次设置易于重复用于回归(自动化)

  • 作为一种协议感知测试,测试结果有助于找到精确、描述性且易于调试的错误

  • 它使您能够找到在定义测试或基于方法的测试中用肉眼无法找到的漏洞

  • 当与黑盒测试、beta 测试和其他调试方法结合使用时,它们能产生最佳结果

虽然模糊测试带来了许多优势,但我们还需要了解和了解一些缺点,这些缺点在此列出:

  • 基于变异的模糊测试可以无限期运行(生成大量测试用例并无限期运行),因此在某些情况下确定最佳测试数量或确定它们运行的时间是否足够长是困难的

  • 测试结果可能会报告没有缺陷,即使运行了大量的测试用例

  • 测试结果可能会报告各种测试用例中的相同缺陷

  • 找出哪个测试用例导致了故障是具有挑战性的

  • 在系统崩溃的情况下,很难找到漏洞

为了总结本节,让我们列出一些在运行 API 模糊测试时可以轻松利用的工具。

开源:

突变模糊测试 模糊测试框架 领域特定模糊测试
American fuzzy lop Sulley Microsoft SDL MiniFuzz File Fuzzer
Radamsa——一群模糊测试工具 Boofuzz Microsoft SDL Regex Fuzzer
OWASP WebScarab BFuzz ABNF Fuzzer
OWASP WSFuzzer - -

上述表格详细介绍了开源模糊测试工具。以下列表提供了一些您可能希望用于模糊测试的商业工具:

  • Codenomicon 的产品套件

  • Peach Fuzer 平台

  • Spirent Avalanche NEXT

  • Beyond Security 的 beSTORM 产品

此外,以下是一些读者可能会觉得有趣的最新工具,可以运行它们的 API:

工具 参考/链接
REST-ler www.microsoft.com/en-us/research/uploads/prod/2018/04/restler.pdf
Burp portswigger.net/burp
Fuzzapi github.com/Fuzzapi/fuzzapi
Fuzz-rest-api github.com/dubzzz/fuzz-rest-api
Big-list-of-naughty-strings github.com/minimaxir/big-list-of-naughty-strings/

回到 API 测试

在前几节中,我们偏离了 API 测试,探讨了安全测试的许多方面。现在,让我们将注意力重新集中,将旅程转向本章剩余部分的一些更多 API 测试方面。

API 测试用例

首先,让我们假设 API 的测试用例需要一些参数或占位符,例如输入参数、预期结果、接收响应所需的时间(来自 API)、解析输入、错误处理和响应格式,以便确定和认证 API 的测试结果。

在我们继续探讨测试用例准备的一些方面之前,让我们先了解一下 API 测试帮助我们检测的可能类型的错误:

  • API 处理错误或不体面的错误条件

  • 任何未使用的标志

  • 缺少或重复的功能

  • 多线程问题

  • 错误处理有效参数值

  • 验证问题,如模式验证或结构问题

  • API 的可靠性问题和性能问题(如超时、连接和获取响应时间)

  • API 漏洞和任何安全问题

API 测试用例和测试用例准备的基本要素

以下列表涵盖了 API 测试测试用例准备的一些基本方面,因为 API 测试的测试用例取决于它们的输出:

  • 根据不同的输入条件和输入组合断言返回值。

  • 当 API 没有返回值时,断言 API 的行为。检查返回代码。

  • 如果底层/目标 API 创建后续事件,断言 API 的事件和触发器。

  • 不仅断言更新数据结构时的 API 结果,还要验证它对系统更新后产生的影响。

  • 当 API 涉及修改特定资源时,通过访问受影响的资源进行断言。

API 测试挑战

如果你准备好了必要的测试用例,开始测试目标 API 是否足够?不,还不够——还有一些挑战你应该知道,在接下来的几节中,我们将探讨你可能会遇到的一些常见挑战以及如何解决这些挑战,当你想要开始 API 测试时。

初始设置

测试基础设施及其稳定性/可用性,以及正常运行时间。在设计阶段开始并实践 API 测试,并确保 API 达到 100%的运行时间是关键。

API 架构更新用于测试

请求和响应架构或格式是 API 的生命线。然而,架构的频繁更改是不可避免的(尤其是在开发阶段),因此也需要为架构配置编写测试用例。在 alpha 和 beta 环境中管理测试可以减少多达 90%的问题(由于架构更新)。

测试参数组合

向 API 添加额外的参数会指数级地增加组合数量,而测试每个可能的参数请求组合以找到特定于配置的问题是必要的。API 发布和确保潜在的 API 候选发布版本可用于测试有助于有效地解决这些挑战。

API 调用序列

在许多情况下,按照特定顺序调用 API 以获得期望的结果是不可避免的,对于这些场景,测试用例也需要实现适当的顺序。然而,在主要 API 中,这很具挑战性,当涉及多线程应用程序的情况时,挑战性更大。除了强制执行候选发布版本外,API 调用序列的可视表示或 API 调用序列的流程图将不仅有助于测试阶段,而且对开发团队也有帮助(作为开发阶段的一部分)。

验证参数

检查电话号码中的数字和数字数量、长度限制、数据类型验证、数据范围修改等验证标准或测试对于测试团队来说是一项艰巨的任务,尤其是对于具有大量参数需要验证的大型 API。实施合成和应用性能监控APM)工具将有助于确保捕捉到由于参数验证而产生的问题。验证参数也是安全测试的关键方面之一。

跟踪系统集成

数据跟踪系统有助于找到对调用的正确响应。然而,对于团队来说,确保 API 测试系统与数据跟踪系统正确工作,并且 API 发出的调用得到正确响应是一个具有挑战性的任务。您可以通过实施和包括持续交付CD)中的负载测试来解决这个挑战。

API 测试最佳实践

以下部分列出了您应该了解的一些 API 测试最佳实践。首先,以下是一些测试用例最佳实践:

  • 按测试类别(单元测试、功能测试、安全测试等)分组 API 测试用例

  • 确保测试用例在每项测试用例的顶部指明涉及的(调用的)API 声明

  • 确保在测试用例中明确提及参数选择

  • 独立执行测试用例,也就是说,每个测试用例都是一个自包含且独立的实体

  • 优先考虑 API 调用,这有助于简化 API 测试

  • 保持测试用例不受测试链(重用其他测试创建的测试数据对象)的影响

  • 测试用例处理一次性调用函数,如删除、关闭窗口、异步调用等,因此需要特别注意以避免不希望的执行

  • 确保 API 调用序列规划良好,并且有明确的执行计划

  • 为 API 的每个可能的已知输入组合创建测试用例,因为这有助于提高底层 API 的测试覆盖率

API 测试工具

完成我们的 API 测试之旅,了解一些常见的 API 测试工具可能是个好主意。以下 API 测试工具的截图提供了每个工具的简要细节,包括其易用性、支持的平台,可能很有用:

图片

CQRS

命令查询责任分离CQRS)是一种由格雷格·杨提出的架构模式。CQRS 建议将系统的读取操作(查询)和写入操作(命令)分离到不同的子系统,因为读取操作最终是一致的,并且从反规范化视图中检索,而命令通常是异步的,并存储在事务性存储中,读取操作最终是一致的,并且从非规范化视图中检索。

因此,通过使用单独的接口或子系统来分离读取和写入操作,不仅有助于最大化 API 的性能,还有助于安全性和可扩展性方面,以及由于更新命令导致的领域级别合并冲突的管理,从而提供更多灵活性。

在我们进一步探讨 CQRS 之前,让我们看看以下图中传统的数据访问模式:

图片

传统的数据源和 DTO 模型(CRUD)

如上图所示,数据的读取和写入操作(源)来自一个数据源或数据存储,包括更新或写入和查询(读取)。

CRUD 方法有一些缺点,如下所示:

  • 被读取的数据和被更新的数据可能有不同的表示形式,并且为了同步管理这些更新数据以服务于读取和写入,这给底层系统带来了开销。

  • 性能受数据存储和数据访问层的高负载影响,并取决于查询以及获取信息所涉及到的复杂性。高量交易还会增加在数据存储中锁定记录并由多个领域访问时的数据争用机会。

  • 管理数据访问的安全性和角色可能也是一个挑战。

CQRS 模式解决了这些缺点,因为它的实现将查询(读取)与更新(命令)操作通过单独的接口分离:

图片

它还检查以下实例:

图片

前面的两个图展示了 CQRS 实现的两种变体。第一个图表示了 CQRS 在单一数据源内具有读取和写入模型的简化设计和实现。请注意,它不是一个单一的数据模型(如 CRUD),而是读取和写入作为独立的模型。这种 CQRS 实现版本的缺点是,与 CRUD 不同,基于 CQRS 的系统无法实现带有脚手架机制的自动代码生成。

具有单独数据源的图描述了另一种 CQRS 设计,其中我们有两个不同的物理数据存储,一个用于写入或更新操作(命令),然后另一个专门用于查询。单独的数据存储实现最大化性能、可扩展性和安全性。

虽然 CQRS 带来了更高的性能、可扩展性和安全性,但它也带来了增加的复杂性和维护开销,以管理由于多个数据存储而导致的致性,同时还需要了解以下列表,以确定何时为您的实现选择 CQRS:

  • CQRS 适用于读写(更新)和读取(查询)数量差异显著的系统

  • 它适用于读取和写入需要单独扩展的系统

  • 它适用于数据一致性不是关键(解决最终一致性)但可用性是关键的系统

  • 对于偏好“发射后不管”事件(异步事件)的系统来说,这是理想的。

  • 它适用于基于事件源的数据存储访问层的系统

  • 它适用于实现领域驱动设计并隔离业务或领域复杂性的系统,并且拥有 CRUD 会使系统更复杂

摘要

我们已经完成了我们的 API 测试之旅,回顾这段旅程,我们学习了 API 测试类型和 API 测试方法的一些基本知识,从单元测试用例开始,到 API 验证测试、功能测试、负载测试和端到端测试,以及 API 监控。我们还触及了一些重要的 API 错误方面,如执行错误、资源泄漏和错误检测方法。

当我们在查看 API 测试时,我们了解到了 REST API 世界中的几个关键安全漏洞,包括敏感数据泄露、身份验证攻击、XSS 攻击、会话固定(CSRF)、DoS 攻击和注入攻击。我们不仅关注攻击,因为本章介绍了这些攻击(漏洞)的原因,以及通过渗透和模糊测试检测这些 API 漏洞的方法和工具。

在了解 API 安全措施之后,我们继续我们的 API 测试之旅——我们了解了一些 API 测试用例创建、API 测试挑战和 API 测试最佳实践的 basics,并以 API 测试工具的快照结束了我们的旅程。

进一步阅读

《精通现代网络渗透测试》 (www.packtpub.com/networking-and-servers/mastering-modern-web-penetration-testing),Prakhar Prasad,Packt Publishing。

第七章:智能应用的 RESTful 服务组合

通过利用边缘和数字化技术及工具,我们个人、社会和职业环境中的各种有形元素都被数字化了。也就是说,这些元素正变得可计算、可通信、敏感、感知、响应和活跃。数字化元素也被称为智能对象或感知材料。进一步来说,日常场所(如家庭、酒店和医院)中的各种嵌入式系统正通过各种通信和数据传输协议进行联网。因此,数字化实体和网络化嵌入式系统的结合使我们的环境变得智能,这些环境通常是自我感知、环境感知和情境感知的。另一个值得注意的趋势是,所有这些赋权工具正越来越多地与远程持有的(云)应用、服务和数据源集成,以便在它们的操作、输出和提供方面得到适当的加强。存在许多集成和经纪平台、适配器、连接器、驱动程序和插件,它们能够实现设备到设备D2D)和设备到云D2C)的集成。这种战略性的转变最终使它们在行动和反应中天生智能。这种宏伟且受技术启发的对我们日常环境中元素和实体的转变,导致了面向服务、事件驱动、洞察力丰富、云支持、容错、任务关键、多方面和以人为本的服务及时制定和交付。在构建和提供这些先进和下一代服务方面,强大的 RESTful 范式的作用正在稳步增长。

本章专门设计,旨在向您介绍 RESTful 服务范式在设计和开发下一代以微服务为中心的企业规模应用以及部署方面的贡献。它探讨了能够相互发现和绑定在一起的 RESTful 服务如何导致过程感知、业务关键和以人为本的复合服务。

本章将涵盖以下主题:

  • 服务组合的需求

  • 各种组合方法(编排和舞蹈)

  • 编排方法

  • 舞蹈方法

  • 向更智能应用发展的编排和舞蹈的混合版本

技术要求

  • 本章主要是一个包含大量理论信息的章节

  • 读者必须熟悉 Java 语言和平台

  • 本书 GitHub 仓库中详细开发并部署了一个简单的服务组合示例

简要介绍 RESTful 微服务

毫无疑问,我们正朝着承诺的知识时代迈进。我们日常环境中的一切普通和日常事物都在被精心填充正确的和相关的智能,以便在操作、输出和提供方面具有适应性、辅助性和灵巧。也就是说,我们个人、专业和社会环境中的所有物理、机械和电气系统都在系统地调整,以成为数字化实体和元素。它们通过一系列值得注意的技术进步获得所需的知识,这些进步是通过应用经过验证的和潜在数字化和边缘技术带来的。信息、通信、感知、视觉、分析、决策支持和执行技术的快速成熟和稳定性对实现智能解决方案、系统和服务的实现做出了巨大贡献。

快速发展的数字技术包括最先进的 IT 基础设施,如软件定义的云、大数据和快速数据集成平台、流式和物联网数据分析、企业系统的移动赋能、未来的物联网IoT)、迷人的区块链技术以及普遍的软件即服务SaaS)现象。知名的边缘技术包括消失的传感器、执行器、多方面的微纳米级电子、小型化标签、垫片、标签、条形码、芯片、控制器、斑点、信标和 LED。

数字技术和边缘技术的无缝结合使得强制性的颠覆、创新和转型能够动态而灵活地趋向 IT 愿景,这关乎创造更智能的家庭、酒店和医院。

微服务架构(MSA)的出现被誉为全球商业和 IT 组织最有趣和最具启发性的事情。目前运行的网页、云和企业管理应用正在通过微服务进行重新设计、重构和修复,以便更加灵活和具有未来感。进一步来说,各种设备,如移动设备(手机、智能手机、数字助理和平板电脑)、手持设备、可穿戴设备、植入设备(如传感器和执行器)、便携式设备(笔记本电脑)、固定设备、游牧设备和边缘/雾计算设备,也正在通过微服务获得能力。也就是说,每个数字化和连接的设备都作为微服务呈现给外界。所有设备的缺陷和差异都通过这种服务赋能得到处理。通常,每个微服务都填充了精心设计的 RESTful API。因此,接口和实现之间存在分离,这是技术无关的。

本章将探讨如何编排和编排具有 RESTful 接口的微服务,以形成高端和以流程为中心的服务。

揭秘微服务架构风格

为了确保通过在信息和通信技术ICT)领域的独特进步中智能利用,确保业务敏捷性和自动化,有一些关键要求。必要的 IT 要求包括最大化团队自主性、优化开发速度、在不妥协一致性的前提下提供灵活性、满足弹性、简化维护。有敏捷编程模型和技术可以加快编写软件应用程序代码的过程,但优雅且可扩展地设计和界定应用程序组件对于软件应用程序长期成功至关重要。不应该有任何供应商锁定。任何技术和工具都可以被利用来生产和维持应用程序模块。此外,应用程序组件必须模块化,以便对企业来说正确且相关,因为企业越来越以服务为导向、以客户为中心、具有适应性和生产力。此外,应用程序组件必须公开可发现、网络可访问、互操作和可组合。因此,MSA(微服务架构)的概念在全球 IT 专家的适当滋养下开始蓬勃发展。微服务是细粒度、水平可扩展、独立部署、API 驱动、可使用和可重用、可移植且技术无关的。

随着容器化运动的稳步推进,拥有容器化的微服务变得容易,这些微服务的镜像可以被下载并提交为在容器中可执行的镜像,这些容器成为最理想的运行平台。如前所述,微服务可以被组合(编排和编排)以创建复合、流程感知和业务关键的应用程序。现在,为了管理容器——在典型的 IT 环境中通常数量庞大——容器化编排平台,如 Kubernetes、Swarm 和 Mesos,正在获得越来越多的关注。随着合格技术和工具的可用性,企业和服务提供商正在建立容器化云环境,以托管、运行和管理以微服务为中心的容器化应用程序。因此,遗留应用程序正在被分割成微服务集合,并且有平台和基础设施来运行它们。也就是说,大型单体应用程序正在云化,以便获得云环境的有战略意义的优势以及强大的微服务架构模式。

微服务优势

微服务日益流行的原因有很多。首先,微服务严格遵循单一责任原则。也就是说,微服务一次只做一件事,但做得很好。MSA 模式要求不同的责任需要放在不同的服务中。这些细粒度和自包含的微服务提供了许多独特的优势。开发、更改和进步的成本低,上市时间短。每个服务都在自己的进程空间中运行,并拥有自己的数据存储。每个服务都必须赋予易于理解和使用的 API。RESTful API 是 API 启用微服务中最受欢迎的。服务通过 API 调用相互通信。为了构建和维持业务关键和企业级应用,必须将多个微服务结合起来。微服务是模块化的(松散耦合且高度内聚)。松散或松耦合的微服务消除了与依赖相关的风险和缺点。另一方面,软件模块的紧密相关责任被保持在一起。

每个微服务实现一个独特的企业功能,因此拥有较小的代码库。因此,服务开发者轻松快速地引入任何所需的更改变得容易。此外,微服务简化并优化了软件设计。微服务还可以分发给测试人员和用户进行初步验证和确认。

微服务满足了软件应用的敏捷设计和开发的多样化目标。进一步来说,正如我们在本书的另一章中讨论的遗留系统现代化,微服务成为现代化和迁移大型单体应用的最佳选择。微服务可以轻松利用云环境的所有固有优势来产生和维持下一代应用,这些应用必须敏捷、适应性强、机智。

此外,微服务的敏捷性、简洁性和机智性使其成为构建和部署业务关键工作负载的最有能力的单元。也因为其规模较小,将错误引入源代码的可能性较低,并且易于调试微服务及其组合。因此,微服务架构具有许多商业和技术优势。专家们持续发布各种最佳实践,以高度有益的方式利用微服务架构(MSA)的独特能力。随着软件应用变得越来越集成和复杂,MSA 带来的舒缓体验有助于委托开发、部署和运营的复杂性。如果明智地使用,MSA 提供了一系列战略优势;其中值得注意的是应用的敏捷设计、支持云启用和本地软件应用,以及实现关注点分离的能力。

云原生应用的出现

MSA 模式成为创建和维持软件应用最有效的工具。为了长期受益,企业正在对两件事进行战略规划——赋予当前运行的应用以微服务为中心,并将这些修复后的应用迁移到云环境中(私有、公共和混合)。也就是说,应用通过利用 MSA 变得云化。

随着云中心的普及,软件工程师和架构师正在设计、开发和部署以微服务为中心的应用于云环境中。这种情况被吹捧为云原生应用。确切地说,为了果断地获得最初设想的所有好处(商业、技术和用户),应用正在通过微服务进行设计,使用其中一个集成开发平台和框架进行开发,并在云环境中部署。基于微服务的软件设计比传统的敏捷编程模型更快,而且有大量方法可用于加快软件构建的过程。

企业 DevOps 工具和流程的持续采用加速了软件应用的持续集成、交付和部署,站点可靠性工程SRE)概念的出现为软件工程带来了额外的自动化和加速。因此,未来属于云原生应用。也就是说,各种企业、移动、社交、嵌入式、交易性、运营性和分析性应用都正在被构建为云原生应用。

物联网设备服务的不断增长的生态系统

我们讨论了特定业务和通用的微服务,这些微服务正越来越多地被容器化。任何软件的容器镜像都可以统一打包、快速交付并在任何软件平台上轻松部署,以无任何障碍地运行和交付其功能。随着各种特定用途和通用的设备快速增长,设备服务也在持续增加,这些服务符合 MSA 规范。我们有各种设备,如医疗仪器、机器人、无人机、商品、餐具、电器、消费电子产品和机器,它们正在逐渐连接起来,以便加入主流计算领域。领先的市场分析和研究报告预测,在未来一到两年内,将会有数十亿个连接的设备。

连接设备的各种功能正通过服务接口被暴露出来。服务范式能够满足不同和分布式设备之间所需的互操作性。也就是说,每个设备都被视为一个可查找的、灵活的、可用的服务。当将设备作为服务呈现给外界时,微服务的健壮性、多功能性和弹性变得非常有用,这消除了所有依赖性引起的挑战。

应用生态系统的变化

主机时代的应用通常是单体和庞大的。它们是集中的、不灵活的、封闭的,且维护成本高昂。然后,我们遇到了客户端-服务器和多层分布式应用。有针对特定业务领域的应用来解决特定问题。有网页、云、社交、企业、移动、可穿戴和物联网应用。然后,还有运营、交易和分析应用。有了微服务的稳固性,各种软件应用都被分解成多个微服务,这些微服务因其便于集成、部署、管理和机动性而非常著名。微服务是自我定义的,因此是自治的。

它们可以独立开发和部署。微服务被填充了它们自己的数据存储。微服务是解耦的,因此不会出现依赖性问题。然而,为了创建和维持更大、更好的应用,需要系统性地找到和融合不同和地理上分布的微服务。也就是说,服务应该被匹配和组合以实现复合应用,这些应用对企业云计算 IT 来说是正确且相关的。不仅对企业应用,而且在创建集成和有洞察力的应用时,组合的行为具有特殊的意义。

组成工作正以两种显著的方式进行——管弦乐和编舞。正如在云原生应用的兴起部分所提到的,存在特定领域和中立的应用程序,它们天生需要融合多个微服务以满足不同的业务需求、市场情绪和用户期望。简而言之,微服务的组合值得最高的认可,因为它能够产生更智能的应用,以实现用户赋能。因此,为了实现各种智能应用,API 丰富的、可扩展的、可配置的、互操作的和可组合的微服务是当务之急。组合工具和技术正在蓬勃发展,因此服务组合是构建洞察驱动、高度复杂和集成应用的最好选择,这些应用可以在任何运行时(裸机服务器、虚拟机、容器和函数)上运行。此外,未来呼唤以设备为中心的云环境,这恰当地被称为雾或设备云。微服务组合不仅可以在资源密集型服务器上运行,还可以在资源受限和网络设备上运行,这是微服务的真正美。

趋向 API 驱动的世界

集成和协作是软件实现业务流程的重要需求。旧的和现代的软件应用程序运行在多种 IT 平台和基础设施上。软件解决方案自动化了大多数业务流程。然而,业务应用程序必须自发地与其他软件包同步,才能产生成果。流程、应用程序和数据必须集成,以确保管理层和最终用户有一个全面而紧凑的视图。沟通是关键。API 是软件组件广泛认可的机制,用于以系统化的方式找到彼此进行交互。API 正在被创建并供应给任何类型的软件,包括 Web 和云应用程序、数据库、中间件和平台。当前的互联网范式正在稳步扩展。曾经,互联网是网络化计算机的网络。也就是说,各种计算机(客户端和服务器)都联网以共享它们独特的功能。

现在,不仅计算机,我们的通信设备和消费电子产品,如智能手机、个人数字助理PDAs)、平板电脑、笔记本电脑、手持设备和通信网关,也加入了互联网。精确地说,物联网IoD)范式正在迅速出现和演变。

各种个人和专业设备、仪器、器具和机器正在连接到互联网;借助众多数字化和边缘技术及工具,我们日常环境中的各种常见和便宜的东西正在被数字化,以便充分赋能以加入主流计算。在适当的技术赋能下,日常物品正变得可计算、可通信、敏感、响应和活跃。现在,随着服务工程技术的稳定性加快,我们将世界视为服务互联网IoS)。也就是说,一切都被表达和暴露为对外界的服务。各种软件和硬件系统,包括嵌入式系统,都呈现为一系列公开可寻址和可用的服务。这最终有助于隐藏系统的复杂性和缺陷。

一方面,我们看到互联网空间中硬件系统(IT 基础设施)、软件应用程序和服务、数据源以及众多平台数量不断增加。然而,为这些互联网连接的系统、应用程序和服务创建和维持 API 并非易事。另一方面,我们有各种数字化的、嵌入式的、网络化的、便携的、时尚的、轻薄的外设设备。因此,为了在底层系统和遥远互联网托管系统之间建立有益的联系,API 是前进的道路。简而言之,RESTful 服务范式通过简化 API 的创建和使用,为系统集成和协作带来了慰藉。

表现层状态转移服务范式

RESTful 服务有助于在基于互联网的系统、应用程序和数据源之间建立有益的关系。RESTful API 使用普遍的 HTTP 方法执行最常见操作(GETPUTPOSTDELETE)。表现层状态转移REST)是设计基于 HTTP 的松散耦合应用程序的具有希望和潜力的架构风格。RESTful 服务是最简单且最受欢迎的服务,它们很好地融合在一起,可以应用于 Web、移动、云和物联网应用程序。

API 设计最佳实践

在创建 API 时,以下非功能性需求NFRs)和服务质量QoS)属性需要给予最高重视,以便开发灵活的 API。其中最流行的是性能/吞吐量、可扩展性、简单性、可修改性、可移植性和可靠性。如果 API 是基于约束来准备的,这些品质是可以实现的。以下是至关重要的约束条件列表:

  • 客户端-服务器架构:这本质上规定客户端和服务器应用程序应该能够独立进化。客户端只需要知道资源 URI。客户端和服务器系统之间不应该有任何其他依赖。因此,客户端和服务器接口的设计可以分别进行。这个限制为开发者提供了急需的灵活性,因为相同的 API 可以在多个后端服务器和数据库系统之间利用。API 可以轻松更改以适应特殊要求,而不会对他人造成任何破坏性影响。

  • 缓存性:众所周知,缓存是将频繁访问的数据的副本存储在请求-响应路径上的多个位置的一种古老做法。当客户端请求资源表示时,请求通常首先发送到最近的缓存以获取数据。如果本地缓存中没有找到所需数据,客户端请求的下一点目的地是代理缓存或请求资源的反向缓存。如果所有缓存都没有最新的数据/新鲜副本,那么就没有其他选择,只能敲击资源本身。使用缓存优化网络可以减少延迟,优化带宽使用,减少服务器负载,并隐藏任何网络故障。

还有其他限制,这些限制在其他章节中有详细说明。

了解服务组合方法

编排和协奏是完成组合服务众所周知且广泛使用的方法。随着容器化运动的兴起,微服务正在被容器化并在云环境中运行。随着容器编排平台的采用,多容器应用程序正在轻松且快速地实现。因此,组合应用程序在服务级别和容器级别得到实现。为了组合,微服务必须找到并绑定到其他微服务,以实现更大的目标和达到更高的目标。一个简单的例子是,当新服务在服务注册表中注册时,它必须调用一些其他服务,例如身份验证和授权服务。根据用例,服务可能需要与更多服务交互,以便与其利益相关者和订阅者相关。

如其他地方所示,每个微服务都包含自己的数据源。每个微服务都启用了自己的一个或多个接口。由于 RESTful 服务范式所展现的日益流行和简单性,大多数 API 都是 RESTful API。以下图表生动地说明了以下内容。服务器端有多个微服务,客户端正在访问一个或多个服务。为了使微服务理念蓬勃发展,服务需要动态集成,以便有目的地促进服务交互:

图片

这里有几个重要的挑战。调用服务不知道被调用服务是否运行良好。由于前所未有的服务请求数量,被请求的服务可能会过载。调用服务不知道服务性能、健康状况和安全信息。此外,这里没有故障处理和补偿机制。当多个服务涉及业务流程和交易时,了解数据和控制流中哪个部分出现问题很重要。必须识别问题的根源,以便简化故障排除。

此外,这些类型的交互通过紧密耦合发生,但紧密耦合可能导致依赖问题。这就是为什么更倾向于轻耦合和松耦合服务:以便开发和部署可以独立进行。此外,服务交互不会产生任何问题。RESTful API 是服务协作的方法。

对一个服务的任何更改都会产生级联影响;随着更多服务的加入,服务环境的操作复杂性必然会升级。因此,专家们提出了有能力的替代方案,通过调用紧密耦合的服务来解决这些不良影响。

服务编排和协奏

编排微服务必须通过编排引擎进行组合。组合必须通过预定义的模式发生,这些模式通常使用编排语言进行描述。编排更适合和适用于实现业务流程。一个合格且符合标准的编排引擎作为中枢大脑,极大地促进了控制和执行的引导和保证。策略/业务规则通过编排引擎以集中方式进行管理和操作。应用流程的 360 度视图通过编排引擎提供。服务和运营级别的协议(SLA/OLA)通过集中引擎进行估算和编码。简单来说,所有集中管理和常用功能都被从参与服务中抽象出来,并集成到编排引擎中,编排引擎还包含业务流程流和管理引擎。

服务链和流程,通常称为服务编排,在传统和企业管理应用集成中提供帮助:

  • 短运行编排:除了处理即时数据和会话外,它们是无状态的同步。它们是一种请求和响应类型。它们以序列化的方式与多个服务进行通信。根据第一个服务的输出,编排器为第二个服务形成适当的消息,依此类推。

  • 长时间运行的编排:这些通常是状态性和异步的。这些涉及人类参与、解释和指令,并通过利用持久数据存储来运行更长的时间。

同样也有一些缺点。编排器和所有相关应用程序组件/服务之间建立了一种紧密耦合。添加新服务和状态需要更新中央业务逻辑。中央协调员有时会成为一个单点故障。

另一方面,编排在多个解耦服务之间实现数据流。每个服务都知道它可以期望和提供什么类型的数据。不需要中央指挥官来运行流程。这完全是关于对等服务组合。每个服务都充满了所需的智能来行动和反应。每个微服务是一个自定义和包含的服务,每个微服务通过消息和事件进行通信并完成分配。事件驱动架构EDA)正在成为日益事件驱动的世界的核心架构风格和模式。编排更适合实现事件驱动应用程序。由于它们的解耦性质,微服务可以被替换和用更好的服务实现替代。

可以在不影响系统的情况下集成额外的服务和它们的实例。新技术和算法可以轻松利用。各种服务依赖及其负面影响正在被消除。在基于编排的服务组合中,不存在单点故障。然而,也有一些缺点。没有中央指挥官或协调员,对服务交互和协作的监控、测量和管理仍然是一个挑战。也就是说,查看相互交谈的服务序列视图是有问题的。使每个微服务在其行动和反应中变得自给自足和智能是困难的,而且在参与服务中实现业务要求和技术驱动的进步也有些困难。编排促进了可扩展性和无共享架构。因此,专家和专家们认为,通过结合这两种架构风格,大多数新兴的应用场景都可以优雅地解决。

正如我们所知,由于各种原因,随着时间推移,业务流程正变得越来越复杂。尽管在一方面发生着向优化方向的过程整合,但业务流程仍在不断增长,变得复杂且运行时间长。流程还必须涉及地理上分散的不同微服务。因此,流程治理和管理变得痛苦。随着贡献微服务数量的快速增长,微服务之间细粒度交互的数量必然呈指数增长。因此,有技术支持的解决方案和最佳实践来克服微服务及其交互中存在的问题。主要方法包括编排和编排。接下来的章节将描述它们的需求、关键驱动因素、功能以及如何使用它们。

从服务编排开始

业务流程是通过各种服务实现来完成的。然而,涉及服务的顺序是有的。通常,业务流程指示要利用的服务序列。编排是通过集中协调器进行服务组合的一种方式。这个协调器/大脑指导并满足业务流程需求,就像管弦乐队的指挥一样——他是作为中央权威领导团队的。通过在各个微服务之间采用这种中介,紧密耦合带来的问题将一劳永逸地消失。也就是说,我们得到了松散耦合的微服务,它们不需要相互了解。它们也不需要知道其他服务是否正在运行。通信可以主要是同步的。也就是说,请求服务应该等待直到它们从请求服务那里得到响应。

存在一个控制器或协调器,充当服务编排器。也就是说,所有服务交互都通过编排器进行。这遵循了众所周知的交互方式:

图片

例如,我们需要两个或更多服务参与并调用以完成一个业务流程。调用顺序在这里也很重要。协调器会对每个服务进行调用并等待请求服务的响应。一旦响应到达协调器,下一个服务序列中的服务就必须被调用,依此类推。更多信息请查看medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c

服务编排的不足

这是一个在需要同步通信和处理时实现更紧密流程控制的好方法。这种方法有几个缺点——如果第一个服务没有响应,其他服务就无法被调用。也就是说,它产生了一种耦合,导致不希望出现的依赖性问题。协调器成为单点故障,因此建议采用集群协调。同步通信会阻塞其他服务请求。

下面的图示说明了通过参与不同和地理上分布的微服务,协调过程是如何完成的:

图片

这种组合模型通常不处理故障情况。大多数时候,服务请求正在被处理。故障率大多是百分之一。最佳实践是,如果出现故障,必须立即启动一种或多种可行的应对措施,例如补偿,以便摆脱混乱。重试和修复活动是确保业务连续性的其他机制。为了最大限度地提高业务利润,必须建立数据和灾难恢复能力以减少数据损失。考虑到服务编排的缺点,服务编排(在后续章节中会详细解释)正受到越来越多的关注。

应用基于编排的组合

如果大部分工作必须按顺序进行(如果完全没有并行执行的可能性),编排就是可行的方法。如果需要保持控制流逻辑集中化,编排就是前进的方向。如果有数百个微服务参与,且控制流不同,集中化是首选。分布式部署和集中管理的箴言通过编排得到了实现。如果解耦不是严格的要求,编排方法就是广泛使用的一种。

从服务编排开始

当构建以微服务为中心的应用程序时,服务依赖必须在萌芽阶段就被遏制。编排会导致依赖,从长远来看这是不好的。因此,必须避免所有类型的依赖,以实现自我定义和自主微服务的战略愿景。专家认为,事件驱动架构(EDA)是解决之前提到的一些挑战的前进之路。也就是说,控制逻辑被塞入编排器模块中,而在这个案例中,逻辑被写入每个参与服务中;逻辑是分布式的,这些授权的服务(智能服务)事先就知道如何对各种事件做出反应。通信是异步的,并且利用事件总线(哑管道)来路由事件。这意味着多个服务可以消费相同的事件,然后启动它们规定的任务。然后,结果被封装并作为事件同时发送回事件总线。如前所述,这些微服务被称为智能实体,事件总线只是一个哑管道。事件总线没有嵌入任何智能,它是事件驱动微服务的主要通信基础设施。

异步特性消除了阻塞或等待,这是编排的主要缺点。在接收到事件后,服务可以产生其他事件,这些事件被循环回事件总线,由其他服务消费以执行其分配的工作。这种解耦在微服务世界中是必需的。

下图展示了事件驱动架构模式是如何工作的:

图片

因此,事件生产者和消费者,在不知道任何关于对方的情况下,以目的为导向的方式合作。由于并行处理(许多服务接收事件并继续处理),确保了服务的快速处理。新的服务可以轻松加入并贡献,而不会影响他人。现有的服务可以轻松地调整以适应业务、技术和用户的变化,这毫无疑问是新的常态。微服务可以独立开发和部署,这赋予了不同的和分布式的开发团队专注于他们的核心活动,以更快地实现和部署以微服务为中心的应用程序。事件流存储所有的事件。假设在事件仍在生成时,某个服务崩溃了;经过一段时间,该服务恢复活力,获取所有错过的事件,并对它们采取行动。此外,这种 EDA(一种反应式架构)使我们能够将读取和写入活动分开。也就是说,这两个活动都可以独立完成。这种分离的优势在于,它们可以根据不断变化的需求独立扩展。如果应用程序读取量很大,那么这部分可以单独扩展,而不会影响写入部分。

通过编排,服务相应地被赋予了根据不断变化的情况行动和反应的能力。服务表现得像舞者——执行分配的动作以适当地对其他舞者做出反应,从而有组织地完成过程。

首先,编排概念依赖于流行的 EDA(电子设计自动化)。编排服务通常对事件做出反应,并将它们的输出放入事件总线队列中,然后,其他授权服务从中取走并开始展示它们独特的功能。"事件总线"是主要的中间件和通信基础设施。这在上面的图中得到了说明:

图片

服务不需要与其他服务交谈来启动和实施一个动作。服务正在等待某些事件在某处被启动并通过事件总线转发给它们,这是主要的消息中间件/代理/总线。

服务编排的不足

编排的系统是松散耦合的、高度灵活的,并且容易适应变化(内部和外部)。作为一个例子,让我们考虑一个订单应用程序。使用反应式架构(事件驱动编排),流程可以表示如下(这是从blog.bernd-ruecker.com/why-service-collaboration-needs-choreography-and-orchestration-239c4f9700fa中摘取的):

图片

支付服务注定要响应订单放置事件。问题是支付服务必须知道它的消费者。如果任何其他服务需要支付服务,我们需要在支付服务中进行更改。现在,业务需求中有一个新的补充。补充是 VIP 客户可以通过发票稍后支付。这个变化将影响多个组件。支付服务只为非 VIP 客户执行支付。

库存服务也必须对订单放置事件做出反应,但仅限于 VIP 客户。以下图表解释了此过程:

图片

这需要引入一个额外的组件,它为服务 A及其用户之间带来清晰的分离。这被称为事件-命令-转换模式

图片

事件-命令-转换模式最终有助于实现解耦的事件驱动系统。

第二个挑战是支付服务可能需要更长的时间来完成。客户必须清偿他们的支付,这可能需要几天或几周。这是一个长期的过程流程,并且必须跟踪其状态。这显然支持编排风格的协作。因此,编排和编舞的融合是提供高度灵活架构以用于复合应用程序的最佳方式。

应用基于编舞的作曲

编舞有助于以并行方式完成业务流程。可以通过添加、降级、替换、替代和退役流程组件来更改流程。如果所有或大部分处理都可以异步进行,这种作曲方法是最合适的。并行执行是反应式架构的主要动机。如前所述,流程逻辑通常在此方法中分布。如果这种去中心化是可管理的,那么编舞方法就是好的。为了创建一个集中和综合的监控、测量和管理视图,最佳选择是使用关联 ID。

编排与编舞的混合

我们已经看到这两种作曲方法都有优点和缺点。也就是说,随着软件平台和包变得更加分布式、集成和复杂,没有一种架构风格在所有情况下都是完美的匹配。因此,IT 专业人士提出了编排和编舞的混合版本来克服这些限制。

第一种混合模式使用服务之间的反应式(用于服务间通信的编排)和内部服务中的编排(服务内通信)。在以下图中,我们有三个服务:A、B 和 C。这些服务通过事件总线相互驱动和反应。服务 A消费一个事件并被触发去编排调用服务 D、E 和 F。这些调用可以是同步的或异步的。然后,从服务获得结果后,服务 A产生一个事件:

图片

A、B 和 C 服务是解耦的。然而,服务 A内的 D、E 和 F 服务是耦合的。这意味着如果这些服务正在进行同步处理,这里会出现阻塞表面。事件总线促进了 A、B 和 C 服务之间的异步处理。每个服务(A、B 和 C)都包含控制逻辑以展示独立的行为。也就是说,逻辑通常分布在多个服务中,而控制、流程和其他横向能力在编排器的情况下被集中化。

第二种混合模式使用服务之间的反应式(事件驱动)和协调器(反应式编排器)之间的协调来协助流程。在这个例子中,它利用了命令和事件的观念。如图所示,协调器向事件流产生命令,而有权执行特定命令的微服务接收到命令,执行所需的处理,然后创建并将事件传递到事件流。

在这个例子中,服务 A 和 C 同时启动,协调器消费事件流中生成的事件并相应地做出反应:

图片

服务通常是解耦的,但服务与协调器之间仍然存在某种耦合。也就是说,协调器必须知道服务需要接收哪些命令才能正确反应。服务之间的事件导致异步处理。整体流程逻辑被放在反应式协调器中。协调器,像往常一样,是一个单点故障。

另一个编排和编排混合的例子

这里是另一个有趣的例子,摘自dzone.com/articles/event-driven-orchestration-an-effective-microservi

编排

如下图所示,一个流程是由用户的事件触发的。然后,通过各自的微服务以编排模式共同完成该流程。这种方法确保了轻量级耦合和高内聚性:

图片

使用消息代理的服务编排

这种方法有几个重要的缺点。编排通常启用去中心化方法,因此所有参与的服务都需要嵌入相关的业务处理逻辑。任何业务和通信逻辑的变化都必须在所有微服务之间共享,并且流程的状态信息必须单独存储。没有集中式服务来照顾所有服务。最后,由于多个分布式服务参与流程的履行,严格的 ACID 属性的实施是不可能的。

服务编排

在网络中还有另一种方法。这是通过整合业务流程模型和符号(BPMN)工作流和 REST 来实现集中式服务编排。以下图表清楚地描述了一个复杂的购物车微服务是如何实现的:

图片

使用 BPMN 和 REST 进行服务编排

如您所见,有三个原始资源类型。这些资源的相应路径细节也刻在前面的图像中。对于单体应用程序,中央服务器会处理所有资源目的地的请求。进一步来说,有一个共享的数据存储库,将资源作为不同的表存储。对于复杂的查询,备受讨论的复杂连接概念就派上用场了。

混合化 - 事件驱动的服务编排

如以下图表所示,购物车服务是通过 BPMN 工作流实现的编排服务。然而,所有各种服务适配器都通过现成的 AMQP 替换,但服务适配器是通过一个通用的现成 AMQP 适配器消除的,这是 AMQP 事件总线的主要组件之一,该事件总线是消息代理,用于解耦服务:

图片

数据管理

MSA 的核心哲学是软件设计和开发的去中心化。去中心化不仅指导业务逻辑的组织,还指导数据如何持久化。

在单体架构中,应用程序组件和数据传统上被集中化。其中一个 SQL 数据库,例如 SQL 服务器,被用作包含多个表的单一数据库。这就是在之前时代数据持久化的方式。甚至一些应用程序逻辑的部分甚至以存储过程、复杂的连接等形式委托给 SQL 服务器数据库。

REST 思维

为了正确且简洁地以去中心化的方式组织数据,RESTful 服务模式非常有用。也就是说,REST 概念引入了一种新的数据建模方式。任何应用程序中都有多个资源。REST 为每个参与资源提供了一个 URL。然后,建议使用标准的 HTTP 动词与资源进行交互。在medium.com/@nathankpeck/microservice-principles-decentralized-data-management-4adaceea173f的文章作者提出了一个小型社交消息应用的示例 API:

图片

有三种原始资源类型——用户、消息和好友。资源类型有一组路径,如下面的图所示。在单体应用的情况下,中央服务器会处理所有资源路径的请求,并且有一个数据库存储所有资源类型作为表:

图片

然而,对于以微服务为中心的应用,每个微服务都有自己的数据库。

这种安排确保了数据的安全。也就是说,每个数据请求、访问和操作都必须通过微服务 API 完成。这里的哲学是微服务与资源类型之间存在一对一的映射:

图片

为每个微服务提供一个独特的数据库/数据库实例带来了一系列优势。

放弃 SQL 连接

对于更好的数据管理,一个值得注意的技巧是避免使用 SQL 连接操作。在 MSA 世界中,每个数据库在逻辑上都是分离的,执行 SQL 连接函数非常繁琐且耗时。这将是一场技术和安全的噩梦。然而,可能会有不同的应用需求。例如,假设消息应用要求时间线视图。也就是说,时间线视图必须显示认证用户每个朋友的最新消息。此外,这个新视图还必须显示朋友的名字和其他细节,以及发送的消息。

使用基本的 REST API 仍然可以实现这个复杂视图。也就是说,客户端必须调用多个不同资源的 API 来满足这个特殊视图的需求:

图片

这意味着总共有五个请求。这是为了降低性能。一种解决方案是向 API 引入一个新的路由:

图片

客户端可以随后获取这个单一的时间线资源以获取渲染时间线视图所需的所有数据。也就是说,它创建了一个额外的、位于三个数据微服务之上的时间线微服务。这个新的微服务将每个底层微服务视为一个资源。这个顶级微服务将底层微服务的数据合并起来,并将合并后的结果暴露给客户端。

在微服务时代,本地数据库操作是通过一个复合微服务完成的:

图片

广泛讨论的性能问题并未出现在这里,因为时间线服务主要与其他三个服务一起托管在容器中。所有这些都在同一台物理机器或附近的机器上。为了进一步减少往返网络延迟,时间线服务可以利用批量获取端点的优势。用户微服务可以有一个端点,接受用户 ID 列表并返回所有匹配的用户对象。这里的优势是,新划分的时间线服务只需向好友服务发起一次请求,向用户服务发起一次请求,以及向消息服务发起一次请求。时间线服务作为一个集中地点来定义逻辑,这种分离有助于在以后的时间点适应任何业务、技术和用户驱动的变化。

最终一致性

这是 NoSQL 数据库的一个众所周知的特点。这对于事务数据库来说不是问题,因为事务数据库是集中式架构的主要数据持久化机制。然而,当数据被分割成许多逻辑或物理数据库时,一致性功能就不能轻易实现了。

例如,考虑一下如果用户在他们的朋友删除账户的同时获取他们的时间线会发生什么:

  1. 时间线服务从好友服务中获取好友列表,并看到一个需要解析的朋友 ID

  2. 好友删除账户,这将从用户服务中删除用户对象,以及从好友服务中删除所有好友引用

  3. 时间线服务尝试通过向用户服务发起请求将朋友 ID 转换为用户详情,但收到404 Not Found响应

很明显,去中心化数据建模需要额外的条件处理来检测和处理在请求之间底层数据发生变化时的竞争条件。对于复杂的应用程序,有必要将一些表放在同一个数据库中以满足数据库事务。想法是这些关联表必须由单个微服务处理。如果相关数据需要强一致性,可以选择使用经过验证的two-phase commit机制。

多语言持久性

分散式数据管理有助于利用多语言持久性。不同类型的数据有不同的存储需求。我们有多结构和大量数据需要批处理和实时处理。对于流数据,有以下流分析平台:

  • 读写平衡:某些类型的数据具有非常高的写入量。这需要不同类型的数据存储。

  • 数据结构:某些高度结构化的数据类型,如 JSON 文档,可能更适合通过文档导向的 NoSQL 数据库进行存储和提供。

  • 数据查询:某些数据可能通过简单的键值数据库轻松访问,而其他类型的数据则需要基于多个列值的复杂查询。

  • 数据生命周期:某些数据只需要很短的时间,可以存储在快速内存数据库中,如 Redis 或 Memcached,而其他数据则需要永久保留,因此使用耐用的磁盘存储。

  • 数据大小:如今,数据大小差异很大。有一些对象存储选项可以容纳令人震惊的数据量。

因此,在确保的微服务时代,数据管理相当不同且具有挑战性。然而,数据专业人士已经提出了合格的数据持久性、表示、交换、掩码、整理、管理和安全解决方案。

摘要

微服务必须通过编排引擎进行组合。组合必须通过预定义的模式发生,这些模式通常使用编排语言进行描述。编排对于实现业务流程流程是相关的。一个合格且符合标准的编排引擎充当一个中央大脑,以引导和保证控制流及其执行。策略/业务规则通过编排引擎以集中方式进行管理和操作。应用程序流程的 360 度视图通过编排引擎提供。服务和运营级别的协议(SLA/OLA)通过集中式引擎进行估算和编码。简单来说,所有集中管理和常用功能都被从参与服务中抽象出来,并集成到编排引擎中,该引擎还具有业务流程流程和管理引擎。

服务链路和流程,通常称为服务编排,正在帮助进行传统和企业的应用程序集成。

“分而治之”一直是调节复杂系统设计、开发、部署和运营复杂性的咒语。微服务架构(MSA)被定位为最高效和敏捷的设计技术。然后还有敏捷开发流程来加速微服务的实现。随着 DevOps 概念的稳定性加快以及众多自动化工具的出现,加速了微服务在云环境中的集成、交付和部署。

为了构建以流程为中心、业务感知、生产就绪和企业级的应用程序,应该识别微服务,根据其能力和专长进行匹配和组合。有两种组合方法——编排和协奏。在下一章中,我们将解释生产有效和可扩展的 RESTful 服务 API 的最佳实践。

第八章:RESTful API 设计技巧

行业垂直领域的企业级应用正越来越多地被构建为一个多语言微服务的集合。由于微服务架构MSA)作为最优化和最有序的应用架构而日益流行,大多数业务关键型应用正被精心设计、开发和部署为一系列独立但交互的微服务。另一个值得注意的趋势是 REST API 在服务和应用中的巨大成功。各种应用(操作、交易和分析)都被配备了 REST API,以便简化应用集成。进一步来说,Web、云、移动和物联网应用都充满了 REST API。

用于设计、开发、调试、交付、部署和退役的现代应用平台也附加到 REST API 上。集成、编排、治理、经纪、合规和管理平台正通过 REST API 向外界公开。由于其简单和轻量级特性,REST 范式大量地吸引了人们的注意力和市场份额。精确地说,每个有价值的微服务、应用和平台都通过 REST 接口进行前端处理。毫无疑问,RESTful API 已经变得深入、普遍和有说服力。也就是说,RESTful API 在构建和集成应用中发挥着至关重要的作用。

本章致力于讨论设计模式和最佳实践,以构建能够轻松应对技术和业务变化的强大且兼容的 REST API。

本章将涉及以下主题:

  • 阐述 API 的重要性

  • 突出 API 设计模式和最佳实践

  • 列举 API 安全指南

  • 解释与 API 设计、开发、集成、安全和管理工作相关的各种工具和平台

  • 趋向 API 驱动的数字世界

技术要求

本章详细介绍了设计高效 RESTful API 的各种最佳实践。读者应具备对 REST 架构风格的一些了解,以及基本的编程经验,以便充分理解和实施本章讨论的最佳实践。

从 API 开始

应用程序编程接口(API)的概念正因其对将任何应用程序或服务暴露给外界以寻找和使用其独特的商业和技术能力所做出的重大贡献而日益占据主导地位。为了实现应用程序、服务和数据的集成,APIs 正被广泛认可为前进的道路。不同应用程序和服务之间进行数据交换。有要求推出高度集成的系统。这意味着必须系统地集成各种第三方应用程序和后端系统。以企业应用中心、企业服务总线、消息代理和队列、API 网关和管理套件以及服务网格形式出现的中间件解决方案,正在显著地用于本地和远程应用程序集成。所有这些集成引擎都配备了精心设计和意图良好的 APIs。

如我们所知,随着大量轻薄、时尚、便携、用途无关以及特定设备的出现,设备生态系统正迅速增长;也就是说,连接设备越来越多地参与主流计算。在这里,API 在使设备、数据源和服务实现数据与逻辑交换方面的贡献也在稳步增长。API 隐藏了设备的外部异构性和多样性。也就是说,每个设备都表达和暴露为一个服务,每个服务都被分割为一个接口和实现。这种隔离在很大程度上有助于设备长时间运行。当涉及到动态地为设备配备额外功能时,API 是不可或缺的。

简而言之,API 是一种信使,它接收并处理请求,并确保企业系统成功运行。社交网站,如 Facebook 和 Twitter,是少数使用开放或公共 API 以利于其业务的公司之一。一个初创公司可以开放其 API,以便第三方软件可以使用其软件。对于集成世界,对于全球企业来说,建立、组合、保护、控制和增强 API 对于向客户、合作伙伴和员工展示其独特能力变得至关重要。随着数字技术在 IT 世界的中心地位,API 的优雅性正占据首位。

了解应用程序编程接口

根据成就卓著的专家们,API 是一套规则和工具,用于启动和监管业务工作负载之间的交互。现在,随着云的快速普及,交互水平已经超越了商业应用。也就是说,大量的 IT 优化和自动化服务也需要相互交互,以便简化并精简业务流程自动化。API 必须独特且统一地找到彼此,启动数据交换,并验证信息,以便软件应用程序能够利用彼此的业务优势。让我们用几个比较来解释 API 及其在我们日益互联的世界中的角色和职责。

如果应用程序是道路上的车辆,那么 API 就是交通规则。规则规定了车辆在道路上必须如何行为。

如果应用程序是食品,那么 API 就是食谱。API 指定并管理各种成分如何融合在一起以创造出美味时尚的菜肴。

如果应用程序是房屋,那么 API 就是蓝图,它阐述了不同的建筑构件如何融合在一起形成一个房屋。

为什么 API 成为了一个主流概念?

在我们日益软件定义和设计的世界中,API(应用程序编程接口)扮演着至关重要的角色,因此加快 API 的生产成为 API 开发者的一项关键任务。也就是说,为 API 的成功制定适当的规则至关重要。API 设计者创建并发布 API,使其他应用程序能够找到并与之交互。

假设你正在公开一个或多个面向客户的 Web 和移动应用程序,这些应用程序运行在云环境中。这些应用程序正被勤奋地配备一个或多个 API。现在,拥有 API 附加的应用程序使得其他开发者能够构建自己的应用程序,这些应用程序可以轻松地与 Web 和移动应用程序集成。这种集成使得应用程序更加庞大和优秀。实施业务流程变得更加容易、快速、无风险且富有成效。客户体验由于全渠道聚合能力的提升而显著提高。无需一个规模庞大的网络公司来设计和开发 API。即使是对于一个想法,一个应用程序也可以通过一个简单的 API 来构建和装饰。应用程序开发者正逐渐习惯于创建和维护 API,以便将他们的应用程序展示给更广泛的受众。

正如我们多次强调的,API 使应用集成变得更加简单。企业集成有其他选择,但在许多方面都显得不足。API 已成为流程集成的标准。当 API 设计者发布一套官方接受且明确定义的规则时,其他人可以自信地开始进行 API 启发的应用程序和平台集成。因此,API 提供了新的可能性和机会。API 赋予开发者控制他人如何使用他们的应用程序的必要权力,同时也规定了他人不能如何使用他们的应用程序。

API 已成为不可或缺的

开创性的技术正在迅速出现并演变,朝着数字化创新、颠覆和转型发展。各种突破正在被发现并普及,以在 IT 和商业领域带来各种加速、自动化和增强。我们正被各种应用架构所淹没,从面向服务的架构SOA)、事件驱动架构EDA)和面向资源的架构ROA)到 MSA。容器已成为服务的默认运行时。云服务器正被表示和暴露为一组容器。随着容器编排平台的日益流行,创建容器集群并使用它们来运行软件应用程序和服务已成为开发、部署和管理软件最显著的方式。正如本书其他地方所解释的,容器和微服务的酷炫融合推动了下一代应用交付。每个微服务都配备了一个或多个 API。进一步来说,每个微服务都有自己的数据存储。为了数据安全和有效数据管理,每个数据库也被赋予了执行数据操作的 API,以确保操作的信心和清晰度。

另一项显著的进步是云环境的快速普及以及每个云中心都挤满了数千个应用程序和平台。云托管平台不仅允许第三方应用程序开发者开发新的应用程序,还促进了与他们的应用程序的无缝和自发的集成。随着我们朝着联邦云环境前进以创建复合应用程序,API 已成为实现快速出现的多云理念最关键实体。设备到设备D2D)、设备到云D2C)和云到云C2C)的集成通过 API 得到促进。

了解主要类型的 API

有几种 API 类型。远程过程调用RPC)是允许应用程序远程调用其他应用程序中的一个或多个函数的一种。XML 和 JSON 是内容类型,并且为 XML 和 JSON 都有 RPC API。在 Web 服务领域,SOAP 凭借针对不同目的的多个标准规范统治着世界。但随后,由于极端复杂性,采用率急剧下降。现在,随着轻量级的 REST 架构风格,每个服务、应用程序、平台、中间件和数据库都在公开 RESTful API。

描述 API 平台

考虑到 API 对于即将到来的数字时代的重要性,工具和产品供应商推出了各种集成平台,以实现 API 生命周期活动。以下各节讨论了不同的平台解决方案,这些解决方案赋予了 API 世界权力。

创建 API 开发平台

有几种方法可以产生高质量的 API。从头开始创建新的 API,翻新现有的 API,以及从当前运行的集成中自动生成 API,这些是众所周知的方法。在这里,我们需要平台支持以其他方式创建 API。有 API 设计和集成平台可以满足这一需求。我们现在有数千个软件即服务SaaS)应用程序通过云环境交付。这些服务通常是 API 附加的。这些 API 可以在多个地方重复使用。然而,这些 API 在功能上可能有限。因此,API 必须定制和增强以满足我们的需求。从头开始构建新的 API 既耗时又容易出错。新构建的 API 必须经过多次迭代才能被归类为相对稳定和成熟。

然而,使用高端 API 设计和集成平台,通过几点击就能加快从现有集成创建 API 的速度。这样,我们不会丢弃功能性的应用程序,并且可以通过利用现有投资节省大量时间。也就是说,API 平台提高了投资回报率RoI)。

通常,客户端团队设计和开发应用程序,服务器端团队为应用程序部署准备后端 IT 基础设施,测试团队被要求测试客户端以及服务器端输出。这些团队应该以协作的方式为项目开发 REST API:

图片

REST API 正随着业务消费者、合作伙伴和员工提出的见解而频繁变化。这些变化必须纳入 API 及其映射的服务中。这无疑会占用时间,因此上市时间必然会增加。为了加速和增强 REST API 的设计、开发、文档和测试,有几种自动化解决方案可供选择,这些解决方案可以显著增强团队协作。

RestCase是一个基于云的 API 开发平台。这个独特的平台使开发者能够协作创建 REST API。这种能力是通过一系列独特的平台模块实现的。关键模块包括一个直观的基于浏览器的界面,该界面可以自动生成文档、测试和模拟。该平台通过创建 API 的模拟来促进快速迭代和测试。

为了让开发团队能够与 QA 和运营团队协作,API 开发平台提供了帮助。这个平台帮助开发团队轻松构建更好的 API。当开发者通过 API 开发平台获得支持时,他们的生产力显著提高。开发时间和成本将降低。API 平台通过共享和与分布式研发团队的协作来增加创新。

API 集成平台

我们正稳步迈向数字时代。日常物品正通过数字化和边缘技术被赋予数字化能力,以成为数字化产品。也就是说,我们日常环境中的每一件事都变得可计算、可通信、敏感、响应和活跃。每个数字产品都被配备了一个合适的 API,以便公开发现、网络访问和易于使用。为了简化 API 集成,最有效的解决方案是 API 集成平台。集成专家和架构师提到了几个用例,说明了为什么企业应该采用 API 集成平台。

随着云计算环境作为一站式 IT 解决方案的出现,为各种商业服务和运营提供支持,预计在未来几年,大多数企业级、个人、移动和 Web 应用程序都将驻留在云端(包括公有云、私有云、混合云和边缘云)。流行的企业级应用包括企业资源规划(ERP)、供应链管理(SCM)、知识和内容管理、人力资源、财务和设施管理,以及资产管理。此外,各种运营、交易和分析应用也在被现代化并迁移到云端环境中,以获得云带来的好处(用户、技术和商业)。

因此,物联网设备、桌面、可穿戴设备和智能手机应用程序连接到云应用程序和数据库变得强制性的。云应用程序、平台和基础设施主要是 API 启用。也就是说,D2D、D2C 和 C2C 集成需求在高度互联的世界中急剧增长。为了获得最佳级的应用程序,API 集成平台的作用是令人敬畏的。

遗留集成

我们从主机时代继承了大量的东西。我们在各个行业垂直领域运行着许多遗留应用程序。银行应用程序仍在运行遗留代码。没有人能否认主机计算机提供了最高的性能。通常,遗留应用程序遵循单体架构。此外,遗留应用程序规模庞大。与遗留时代相关联的还有几个其他缺点。尽管如此,由于它们的独特能力,企业对现代化遗留投资的步伐相当缓慢。这促使在现代和遗留应用程序之间建立集成管道。遗留系统携带和存储大量的业务交易数据。API 集成平台使数据集成成为可能。也就是说,数据可以被提取、转换并加载到与相关联的新应用程序关联的数据库和仓库中。为了生产和维持集成系统,获取和使用遗留数据是强制性的。API 集成平台对于这种持续的需求非常有用。

API 集成平台促进了复合应用程序的形成。随着 MSA 成为应用架构中最明智的选择,通过编排、编排或混合方法进行组合是生产过程感知和业务关键应用程序的重要任务。众所周知,每个微服务都暴露了一个 API。因此,为了将多个微服务组合成复合应用程序,API 集成平台的贡献是多方面的。

API 集成平台确保了更快的云采用和未来保障的企业集成。您需要一个 API 集成平台来保障企业集成。以下是在未来几年内的预测:

  • 数以百万计的软件服务

  • 数以亿计的连接设备

  • 数以万亿计的数字化文物

随着每个物理的、机械的、电的和电子系统都配备了 API 并以服务的形式呈现,API 集成平台解决方案的采用率在未来几年必将上升。

API 管理平台

随着企业为最终用户、合作伙伴和员工构建企业级和移动应用程序,API 变得不可或缺。应用程序必须在任何类型的网络上表现良好。API 提供了对托管在云基础设施中的数据服务的设计时间和运行时访问。企业主要是交易性的。移动商务和业务应用程序的交易性日益增加。因此,我们需要 API 管理平台来精确管理 API。

我们已经讨论了 API 开发和集成平台的各种特性和需求,以及它们如何帮助软件开发人员和集成人员自信且清晰地构建集成系统。现在,我们正朝着 API 管理和通过管理平台实现其自动化迈进。一般来说,API 管理涉及设计高质量的 API,然后发布和分析它们,以便持续跟踪其使用情况。API 可以是面向内部或外部的,它们必须易于搜索和消费。随着世界越来越倾向于拥抱 API 经济,有多个产品和平台简化并优化了 API 管理活动。API 管理解决方案的主要目的如下:

  • API 设计:API 管理解决方案赋予开发人员和外部各方设计、生产和部署 API 所需的所有知识和能力。进一步来说,它有助于制作 API 文档,设置安全策略、服务级别和运行时能力。

  • API 网关:我们专门分配了一章来描述 API 网关的各种功能以及微服务如何通过利用 API 网关解决方案受益。如今,API 管理解决方案也被赋予了作为 API 网关的能力,这为任何服务用户提供了一个集中式和集群式的前端,以便利用分布式微服务。API 网关充当所有下游 API 的守门人,并以安全的方式调节服务交互。

  • API 存储:API 应该存储在集中位置,以便内部和外部用户可以利用它们。因此,作为服务市场,API 市场正在快速发展。

  • API 分析:随着 API 使用量的增加,跟踪关键指标,如 API 使用情况、交易和性能,变得非常重要。捕捉这些决策支持和增值数据,并对其进行处理,有助于提取有用见解,以提升服务质量。

端到端 API 生命周期活动(如身份验证、为用户提供角色配置、政策建立和执行、用户请求速率限制、API 数据分析、监控)正由管理平台解决方案负责处理。这些解决方案还通过开发人员、外部方和员工来启用和增强 API 的使用。API 文档也正在实现自动化。API 管理套件使用缓存来减轻服务负载。API 日志、运营、性能、可扩展性和安全数据通过 API 分析功能收集并受到各种调查,以便了解服务状态,从而进行调优。几乎所有的企业都在拥抱 API,以在线运营并追求市场覆盖率的提升。进一步来说,API 目录正在在公司门户中发布。灵活的解决方案是部署 API 管理平台,以显著提高业务敏捷性和适应性。

API 必须进行艺术化的架构设计。API 不仅仅是为了使每个软件包都能有一个面向集成的前端,它们还必须极其用户友好。API 端点必须易于理解和使用,以便执行基本任务。API 预计将显著提高开发者的生产力。学习设计模式必须快速且简单,以便赋予开发者创建良好设计的 API 的能力。API 必须设计成持久性的。也就是说,必须确保 API 的一致性。API 质量也必须进行严格的验证和验证,否则,有缺陷的 API 会浪费开发者大量时间。API 提供者和消费者都要求高质量的 API。通过 API 设计、部署和管理自动化工具,全球企业正在实现更好和更一致的结果。

API 已经展现出了成功的曙光,并被用于满足应用集成,这对于企业 IT 团队来说一直是个头疼的问题。API 被视为满足各种集成需求的既定前进方式。除了网络和云服务能力之外,移动服务能力的实现过程正在获得越来越多的关注和市场份额,以便提供随时随地、任何设备、任何网络的信息和服务访问。API 的设计不仅要应对现有设备,还要考虑未来设备。市场上不断涌现出许多艺术设计的 I/O 设备。除了软件系统之外,每个连接的设备和集群基础设施都通过附加高效且可扩展的 API 来实现,以便被发现和绑定,从而创造商业价值。

您可以使用多种技术和工具来安全且高效地加快基于 API 的集成。如前所述,我们已整合了大量的平台解决方案(包括开源和商业级)以促进 API 经济。这些平台本质上保证了所产生和使用的 API 的质量。这种质量是强制性的,以获得 API 现象的好处。

揭秘 RESTful 服务范式

本书深入探讨了 RESTful 服务和 API。尽管 REST 风格简单,但它是一个功能齐全的架构风格。对于 IT 专业人员来说,生产、公开和维护高质量的 RESTful API 以实现平滑的功能集成是一项关键且具有挑战性的工作。主要来说,REST 是通过 HTTP 协议实现的。然而,REST 并不局限于 HTTP。REST API 是为资源实现的,资源可以是实体或服务。这些 API 提供了一种通过其 URI 来识别资源的方法。URI 可以用来传输资源表示的当前状态。API 可以表示为一组端点,其中包含动词和名词。动词通常表示一个动作,如 get、put 或 delete,而名词则表示与动作相关的参数。始终有一个机制来传达错误消息和成功的执行是一个好习惯。API 必须清楚地阐述其服务和参数,以减少开发者的错误。错误消息也必须全面,以便明确地向最终用户传达正在发生的情况。

随着服务范式的广泛应用,RESTful 服务的需求激增,与 SOAP 服务相比,RESTful 服务相当轻量。面向服务的应用程序开发和组装已成为软件工程的事实标准。对于开发和部署互联网应用程序,利用 RESTful 服务作为应用程序组件的需求急剧增长。RESTful 应用程序和服务配备了兼容且高效的 API。RESTful 服务为应用程序开发带来了急需的敏捷性、适应性和简单性。API 已成为 IT 服务和业务工作负载中如此常见的元素。此外,API 是软件基础设施、中间件解决方案、集成服务器、容器化平台和后端数据库系统中最常见的元素。API 作为企业应用程序以一致和认知方式交互的标准化机制。

由于云原生和启用应用程序的显著增长,有必要在云托管应用程序和数据源与企业和个人应用程序之间建立无缝和自发的链接。在这里,REST 范式在其它选项中表现良好。我们有大量的编程和脚本语言用于客户端和服务器端应用程序。不仅桌面和笔记本电脑,还有手持设备、可穿戴设备、便携式设备、游牧设备、无线设备和移动设备正在与 Web 应用程序集成。环境变得高度复杂和异构。此外,许多设备和服务必须协作以完成业务流程。REST 范式在这里提供了帮助。REST 概念是在不同环境中运行 Web 应用程序的抽象,例如 Windows 或 Linux。

RESTful 服务保证了使用不同编程语言和平台编写的应用程序所需的灵活性,以托管和运行这些应用程序。异构应用程序正在通过 RESTful 服务的力量实现相互操作。设备正被暴露为设备服务,并通过 RESTful API 进行前端处理。另一个普遍的趋势是云应用程序由 REST API 驱动。不仅资源密集型系统,而且资源受限的嵌入式设备,都从 RESTful 服务的范式中受益,以便连接。存在几种以设备为中心的通信和数据传输协议,REST 现象正在使用它们成为服务开发者和用户的宠儿。为了在许多使用场景中实现巨大的善意,对这种服务实现概念施加了几个约束。

特征化 REST 架构风格

首先,REST 范式是一种架构模式。专家们发布了许多设计模式,以设计和开发 RESTful 服务。有集成和部署模式,可以快速实现开创性的 REST 范式。

REST 范式符合著名的 RoA 模式。应用程序状态和功能被系统地划分为分布式资源。这些资源在线可用,因此每个资源都可以使用普遍的 HTTP 命令(GETPUTPOSTDELETE)进行访问和使用。如果我们想在文件服务器上放置一个文件,我们需要使用PUTPOST。如果我们想从服务器获取文件,我们可以使用GET命令。如果我们想删除文件,DELETE命令是我们的首选选项。REST 架构,像往常一样,是客户端-服务器和分层的。它支持客户端缓存。此外,REST 应用程序是无状态的。这意味着它们不存储应用程序的状态。以下是对其独特特征的解释:

  • 客户端-服务器:客户端可以是任何向服务器发送服务请求的东西。服务器端将托管 RESTful 服务,为客户端提供业务功能。

  • 无状态:服务器不存储客户端会话信息。客户端必须与服务器共享所有必要细节以获得适当的答案。也就是说,RESTful 服务是自我定义的、自包含的、自主的、高度可扩展的,并且性能良好。

  • 缓存:由于服务器不存储客户端信息,因此客户端有责任保留所有相关信息。因此,缓存的概念应运而生并变得流行。有时,客户端会再次向服务器发送相同的请求。由于 REST 服务是独立的,客户端将得到相同的响应。为了减少网络流量,客户端侧引入了缓存的概念。由于缓存存储了之前的响应,并且缓存位于客户端侧,因此获取服务响应的速度更快。

缓存就是将频繁访问的数据的副本存储在客户端和服务器路径上的多个位置。通常,客户端请求会被传递到世界另一端的服务器。然而,一系列的缓存被整合在客户端和服务器组件之间。也就是说,请求首先敲打客户端侧的本地缓存,然后是服务器侧的反向代理。如果任何一个缓存有最新的资源表示,它就可以被客户端使用。如果没有,请求将被发送到服务器组件以获取最新信息。

  • 分层系统:由于企业应用复杂性的不断增加,引入了分层。这意味着可以在客户端和服务器之间集成额外的模块。我们熟悉三层和多层应用。这是一种复杂性缓解技术。

REST 资源表示压缩

REST API 可以以多种格式(XML、JSON、HTML 和文本)返回资源表示。资源表示可以被压缩以节省网络带宽和存储需求。存在不同的传输协议可以实现压缩,并且客户端相应地被通知所使用的压缩算法。

超媒体作为应用状态引擎HATEOAS)是 REST 范式的一个重要约束。术语 超媒体 指的是指向各种类型的多媒体内容(图像、视频、音频或文本)的链接。这种架构风格有助于在响应消息中使用超媒体链接,以便客户端可以动态地访问正确的资源。

允许幂等的 REST API

如果我们多次发送相同的请求并且每次都收到相同的响应,这些 API 通常被称为 幂等。一些 API 消费者有意或无意地两次或三次发送相同的请求。API 必须理解这一点,并以相同的响应进行回复。

重点是,我们必须构建和部署 智能 API。它们的一个特点是幂等 API。 我们必须为所有标准 HTTP 操作,如 GETPUTDELETE,拥有 *幂等的 REST API。只有 POST API 不会是幂等的。

如前所述,RESTful 通信发生在 HTTP 协议上,HTTP 命令正被 REST 客户端用于与 REST 服务器交互。这种一致性和简单性对于更快、更轻松地采用 REST 架构模式大有裨益,这在日益互联的世界中成为一个重要的概念。

REST API 设计考虑因素

随着 API 的使用量迅速增加,API 设计过程现在正受到来自各种来源的广泛关注。不仅需要深入规划和实施高度优化和有序的 API,还需要使它们能够适应 Web、移动和云世界,具有弹性、健壮性和多功能性。API 的可用性对于应用程序的持续运行至关重要。API 的高性能和吞吐量至关重要。用户请求的数量、交易的数量以及处理的数据量对于 API 为应用世界贡献力量至关重要。API 必须具有良好的可扩展性,以便自动处理额外的用户和数据负载,从而对消费者和利益相关者具有相关性。不应有任何由 API 引起的应用程序速度减慢和崩溃。我们已在单独的部分讨论了 API 安全性的重要性。任何类型的内部错误、安全漏洞或 API 的漏洞都必须通过测试程序和工具进行清除。任何类型的外部攻击也需要给予足够的考虑。

应用程序必须完全遵守所有声明的 API 要求。也就是说,应用程序必须发送正确的数据和协议,正如 API 规则手册中所刻画的。如果存在任何故意的偏差,结果可能是无法修复的损害。因此,API 监控、日志收集和熟练的管理对于成功至关重要。在设计 API 时,服务架构师和 API 开发者必须认真考虑以下因素。API 在满足 RESTful 服务的 非功能性需求NFRs)/服务质量QoS)属性方面发挥着至关重要的作用。众所周知,RESTful 服务保证了简单性和普遍性。因此,在设计 REST API 时,架构师和设计师必须特别重视以下 NFRs:

  • 性能:这是设计用于 RESTful 服务交互和协作的 API 时的一个重要参数。

  • 可扩展性:RESTful API 必须设计成支持大量应用程序组件。这些组件之间的交互数量也应该相对较高。

  • 可修改性:新技术不断涌现,商业情绪不断变化,用户期望也在不断演变。由于变化是唯一的不变因素,API 必须构建以适应不断变化的需求。

  • 便携性:API 正在 API 存储库中被使用、测试、精炼和部署。API 必须具备便携性才能在各个系统间工作。

  • 可靠性:API 必须可靠,以承受系统级别的任何类型的故障和错误。组件和数据级别可能存在故障,但系统仍应继续运行并履行其义务。

当我们努力构建和部署高度可扩展、可用和可靠的系统时,API 作为满足先前 NFRs 的舒缓元素的出现,正受到赞赏和欢迎。

枚举 RESTful API 设计模式

API 设计正成为任何 API 产品战略的核心支柱,以实现软件产品的预期成功。任何软件包都必须附加适当的 API 才能远程被发现和绑定。公开 API 有助于任何第三方服务或软件提供商建立联系并被使用。因此,生产易于使用、前瞻性和可持续的 API 对于 API 驱动世界至关重要。良好的 API 设计可以显著提高开发者体验DX),并可以提高性能和长期可维护性。API 实现通常对 API 客户端隐藏。这种分离为 API 实现带来的任何进步提供了所需的灵活性,而不会影响任何 API 客户端。一个设计良好的 API 必须具有以下属性:

  • API 必须支持所有标准和相互同意的平台

  • 随着设备生态系统的不断增长,任何客户端都可以调用和使用 API

  • 什么都不会是静态的

  • API 更改是强制性的,因为应用程序和服务注定要不断现代化

  • API 设计和修改必须以这种方式进行,以确保客户端不受负面影响

媒体类型

正如众所周知,客户端和服务器通常交换资源表示。通常,在POST请求中,请求体包含要创建的资源表示。相反,在GET请求中,响应体包含接收到的资源表示。通常,格式通过使用媒体类型(也称为 MIME 类型)来指定。对于非二进制数据,它是 JSON(媒体类型=application/json)和 XML(媒体类型=application/xml)。请求或响应中的 Content-Type 头指定了表示的格式。

在理解了 API 对于创建复合企业的重要性之后,IT 专业人士已经挖掘出许多 API 设计模式,这些模式易于 API 设计者和开发者理解和使用:

  • 无状态:对于 Web 规模的应用程序,在任何时候,使用各种客户端设备通过 REST 服务获得服务的用户数量都在数百万。通过 RESTful 服务创建的 Web 应用程序必须内部和外部可扩展。因此,在应用程序服务器中存储客户端会话信息可能会降低应用程序性能。也就是说,应用程序服务器必须是无状态的。API 必须设计成支持无状态属性。

  • 内容协商:我们知道,一个资源可以有多种表示形式,以满足不同客户端设备的不同需求。客户端请求合适的表示形式被称为内容协商。另一种选择是,可以使用不同的 URL 来引用不同的表示形式。

  • 统一资源标识符URI模板:包含占位符的 URIs。通过使用命名替换变量,模板可以用来为特定资源创建 URIs。模板用于规范文档中描述资源的位置。客户端应该了解这些模板,以生成任何资源的完整 URI。这是由于资源通常不会链接到其他资源。决定资源 URI 的责任在于客户端手动。解决这种困境的正确方法是,从已知位置链接这些资源。单个产品提供必须从中央产品列表资源链接。因此,URI 模板对于明确标识任何资源的 URI 非常有用。

  • 版本控制:这是一个重要的实践。APIs 不断变化以适应商业和技术变化。API 兼容性是一个严肃的问题。开发者们持续地被要求在 APIs 上引入经过验证的更改。有时,为了能够与较旧版本的 APIs 一起工作,需要向后兼容。其理念是,如果版本号与 API 相关联,则不会有冲突,并且简化了用户的工作。API 版本控制也有助于精确跟踪 API 的演变。

  • 批量操作:服务正在通过细粒度和粗粒度方法进行装饰。批量操作通常通过粗粒度方法实现。API 设计者必须注意这一点。细粒度方法通常需要多个请求和响应,这浪费了大量的网络带宽资源。通过少数几个请求,可以完成更大更好的操作。

  • 分页: 通过单个 URI 暴露多种资源可能导致应用程序为客户端获取大量数据。不可能在单页面上显示所有表示细节。分页在这里发挥作用,可以减少将传递给客户端的数据量,从而节省网络带宽。这种模式还可以避免服务器端不必要的处理。在 API 设计阶段,尽管预测作为响应返回的数据量是一项具有挑战性的任务,但对于 API 设计者来说,预测和规划分页资源需求是必要的。

  • 排序: 对于返回大量数据给客户端的任何 API 端点,排序都是一个重要的功能。为了便于排序功能,许多 API 添加了sortsort_by URL 参数,它可以接受字段名作为值。好的 API 设计允许您在排序时指定升序降序。排序参数应包含执行排序的属性名称,用逗号分隔。

  • 过滤: 这是一个公认的说法,即 URL 参数是嵌入基本过滤到 REST API 的方式。如果我们有一个/products端点,它是销售的产品,我们可以通过属性名进行过滤,例如GET /products?state=activeGET /products?state=active&seller_id=1234。这里的问题是这仅适用于精确匹配。如果我们想要过滤范围(价格或日期),就存在挑战。URL 参数通常有一个键和一个值,但为了执行高级过滤,我们需要至少三个组件:属性/字段名、运算符和过滤值。

有方法将这些组件编码到 URL 参数键/值中。您可以在以下链接中找到更多详细信息:www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/。通过限制具有特定和有限属性及其预期值的查询(资源)数量来限制资源或响应称为过滤。因此,可以在多个属性上应用过滤器,或者允许一个过滤属性有多个值。

  • Unicode: 当今的 API 必须支持比英语字符更多的字符。如果 URL 中嵌入 Unicode 字符,API 必须相应地开发。

  • 错误日志: 必须仔细收集各种错误消息,并对其进行各种调查,以从日志中提取任何有用的见解。客户端请求可能有错误。可能是由 API 本身引起的错误。因此,日志收集是错误分析的重要过程。

  • 无状态身份验证和授权: 如多次提及,REST API 必须设计为无状态的。每个请求都必须是自包含的,以便在没有客户端知识的情况下满足用户请求。如果主要服务无法进行交易,其各种实例必须及时启动以实现请求。这不仅适用于服务请求,也适用于用户身份验证和授权,无状态服务需要参与和调用。通常,用户信息存储在服务器端,以便后续请求不需要访问身份验证服务。然而,这种方法未能通过可扩展性测试,因此每个请求都必须携带所有相关信息,以便进行身份验证和授权。这不仅仅限于用户——服务到服务的授权也通过这种方法得到促进。

专家建议使用 JWT 与 OAuth2 进行用户身份验证。对于服务到服务的通信,在请求消息的头部组件中具有加密的 API 密钥至关重要:

  • Swagger 用于文档: API 文档对于开发者深入了解 API 及其独特功能非常重要。API 文档的准确性对于预期的成功至关重要。与其使用人力资源生成 API 文档,不如使用自动化技术和工具来创建 API 文档更简单、更快。API、注释和元数据共同促进了文档的自动化生成。Swagger 是广泛使用的用于记录 REST API 的工具。生成的文档包含有关特定 API 使用的详细信息。它还提供了 API 中方法输入和输出信息的相关细节。

  • HTTP 状态码: 网上有很多文章和博客列出了 HTTP 状态码。这些帮助客户端了解服务器面临的实际情况。也就是说,当客户端请求到达服务器端服务时,服务器将向客户端生成一系列响应。可能会有失败或成功。状态码提供了一些关于服务器端发生情况的线索。

  • HATEOAS: 每个 HTTP GET请求都必须提供响应消息中的所有信息。这些详细信息有助于通过嵌入在响应中的超链接找到与请求对象直接相关的各种资源。此外,它还必须包含描述在每个资源上可用的相关操作的详细信息。这被称为 HATEOAS。HATEOAS 简化了通过资源及其可用操作的导航。这促进了客户端与应用程序进行不同操作的交互。所有元数据都嵌入在服务器的响应中。

我们已经讨论了大多数 API 设计模式,这些模式来源于多个来源,例如经验丰富的从业者撰写的博客。有关应用、服务、设备、中间件和数据库 API 的风险免费和有价值的体验,有设计模式、最佳实践、指标和其他知识指南。

API 安全设计模式

API 安全至关重要。如果允许对 API 进行任何操作,结果可能是灾难性的。以下是一篇文章,列举了 API 开发者必须细致入微地做的事情,以便达到无法渗透和无法破解的 API:dzone.com/articles/top-5-rest-api-security-guidelines。首先,正在进行身份验证和认证,以便用户能够访问 RESTful API。接下来是授权。RESTful API 设计必须以这种方式进行,以便可以建立和执行授权权利。此外,将正确的更改引入授权策略/规则也必须是 API 设计的一部分。必须强制执行基于权利的资源访问。

确保设计要求对特定资源集合和操作进行 API 密钥或会话令牌验证是 API 设计者/开发者的责任。例如,如果有一个针对书籍的 API,那么允许任何用户删除条目是不明智的。但是,允许任何人获取书籍目录条目是可以接受的。进一步来说,会话令牌或 API 密钥必须嵌入到消息体中或作为 cookie 发送,以仔细保护特权集合或操作免受未经授权的使用。

白名单允许的方法

我们都知道 REST 服务允许对资源执行多种操作的方法。为了避免任何冲突,RESTful 服务必须开发和部署,以确保只接受正确的处理方法。其他方法会自动返回适当的错误消息。关键的安全攻击如下详细说明:

  • 跨站请求伪造:REST 服务向外部公开资源,以及一个精心设计的 API。保护PUTPOSTDELETE请求的跨站请求伪造CSRF)至关重要。标准的保护方法是使用基于令牌的方法之一。如果我们的应用程序中存在任何跨站脚本XSS),即使我们使用随机令牌,CSRF 仍然可以轻易完成,因此,专家建议利用可行的机制来防止 XSS。

  • 输入验证:我们在客户端和服务器端都执行验证。客户端和服务器端的脚本语言具有验证请求和响应的内在能力。如果存在不正确的用户输入,最好拒绝它。此外,记录输入验证失败也是更好的做法。如果有更多的输入验证失败,可以考虑对 API 进行速率限制。

  • URL 验证:攻击者可能篡改 HTTP 请求的任何部分以破坏所采用的安全方法。构成 HTTP 请求的关键部分包括 URL、查询字符串、头、cookie、表单和隐藏字段。

  • 安全解析:所有传入的消息都必须系统地解析以检查任何安全违规行为。确实,REST 可以接受通过多种机制(包括 XML 和 JSON)封装的消息。

  • 验证传入的内容类型:当我们使用 POSTPUT 方法提交新数据时,客户端应明确指定内容类型(如 XML 或 Java)。

  • 服务器永不假设内容类型:服务器始终需要验证内容类型和内容是否相同。如果它们不匹配,必须返回适当的错误消息。

  • 验证响应类型:这是另一种验证。REST 服务允许多种响应类型,因此客户端必须在请求消息的 Accept 标头中明确说明响应类型的首选顺序。此外,对于典型的响应类型有许多 MIME 类型,因此客户端必须指定在回复消息中应使用哪些 MIME 类型。

  • XML 输入验证:存在针对 XML 的特定攻击(如 XML 外部实体和 XML 签名包装),因此基于 XML 的服务必须安全地解析 XML 消息以防止攻击。

  • 安全头:为了正确解释服务器消息,服务器必须将正确的内容类型嵌入到内容类型头中。

  • XML 编码:应使用 XML 序列化器构建 XML 消息。然后,只有浏览器可以解析 XML 内容,并且不会出现任何 XML 注入错误。

  • 加密:当数据正在传输时,它会被加密。也就是说,必须启用传输层安全(TLS)。当凭证、更新、删除以及其他增值信息传输时,TLS 非常重要。即使是专家也推荐使用相互认证的客户端证书来确保 RESTful 网络服务的最高安全性。同样,当数据被持久化时,数据必须被加密,当任何应用程序使用数据时,必须采取适当的安全措施。

  • 消息完整性:密码学确保了机密性,但我们还需要消息完整性。也就是说,我们利用消息摘要/哈希算法来实现消息完整性。JSON Web Token(JWT)是一种标准化的、可选验证的、加密的容器格式。它用于在双方之间安全地传输信息。

JWT 定义了在网络中传输的信息的结构,它有两种形式:序列化和反序列化。序列化形式用于通过网络在每个请求和响应中传输数据。反序列化形式用于读取和写入令牌中的数据。JWT 在以下场景中很有用:

授权:JWT(Jason Web Tokens)允许登录用户访问路由、服务和资源,因为 JWT 令牌将是后续传入请求(在初始请求之后)的一部分,JWT 也是 SSO(单点登录)实现的热门选择。

信息交换:JWT(JSON Web Tokens)正在成为一种在各方之间安全传输信息的方式。JWT 可以使用公钥和私钥进行签名,因此很容易理解发送者是谁。此外,通过消息摘要,可以证明消息在传输过程中没有被篡改。

API 安全性是一个重要的现象。有越来越多的最佳实践和模式用于保护 API。在可视化任何类型的安全影响时,收集错误日志很有用。随着时间的推移,这是确保 API 设计、发布和维护无懈可击安全性的最可靠途径。

总结来说,开发者应该努力理解当前的环境和未来趋势。一开始就提出一个战略上可行的 API 并不容易。如果充满热情并经过讨论,API 可以成就或标记整个场景。API 设计者必须从用户的角度开始思考。稳健的设计是生产并维持最先进和多功能 API 的关键因素。设计不佳的 API 可能会导致失败,或者客户可能对应用程序和服务不满意。

摘要

微服务架构是生产多功能和企业级应用程序的主要架构模式和风格,这些应用程序可以优雅地托管和运行在云环境中(本地和远程)。微服务轻量级、易于构建和部署、自我定义、细粒度,并且可网络访问。它们还遵循单一功能原则。

现在,所有通过 HTTP 通信并使用 JSON 或 XML 格式消息或 HTTP 方法(GETPOSTPUTDELETE)的都被称为 RESTful API。RESTful API 易于设计和构建。微服务和 RESTful API 的无缝和自发的结合开辟了新的可能性和机会。因此,在我们的日益互联和以服务为导向的世界中,设计高质量的 RESTful API 具有特殊的重要性。本章提供了相关模式、最佳实践和一般指南,以制定高质量的 RESTful API。

进一步阅读

第九章:对 RESTful 服务范式的更深入视角

随着一群未来派和灵活的信息技术、自动化工具、优化基础设施、集成平台和多功能设备的盛大到来,世界必将尝试和体验以前未知的软件应用和服务,以赋能企业和个人。随着轻薄、时尚、便携、潮流的智能手机的快速普及,我们注定将拥有令人眼花缭乱的易于使用和引人注目的移动应用和服务。随着可穿戴设备、便携设备、固定设备、手持设备和无线设备的加入主流计算,应用的范围、规模、速度和结构必将显著提升。为了将应用适配到各种设备,全球软件开发者预计将通过利用潜力巨大的编程语言、平台、工具包、软件开发方法、设计模式和最佳实践,推出可定制、可组合和可配置的应用。因此,软件领域正以极大的信心和清晰度持续扩展。因此,开创性的软件包、本土应用、一站式解决方案、洞察力平台和流程感知复合软件的推出和影响都在按预期进行。也就是说,我们正趋向于软件定义的世界——软件是人类光明的未来的关键齿轮。

本书,尤其是本章,致力于解释如何快速、轻松地生成 RESTful 服务和它们相应的 API 的新兴技术和技巧。本章有以下目标:

  • 软件定义和驱动的世界

  • 描述新兴的应用类型

  • 应用现代化和集成的 REST 范式

  • 数字转型和智能的 RESTful 服务

  • 基于 REST 的微服务最佳实践

技术要求

本章没有特殊的技术要求,因为它不涉及设计、开发或部署软件包。其他章节描述了如何设计和使用面向微服务的 RESTful API。进一步来说,几章解释了为应用和数据集成生产独立和实用型 RESTful 服务的细节。本书还详细介绍了 API 设计技术和最佳实践。本章进一步阐明了神秘的 RESTful 范式如何将成为软件定义世界即将到来的时代的趋势 setter。其他章节中提出的 RESTful 技术、工具和技巧对这一章节也很有帮助。确切地说,突破性的 RESTful 理念的角色和责任必将提升,随着世界趋向于一个完全由适应性强的、灵巧的软件丰富、启用和赋权的世界。

趋向于软件定义和软件驱动的世界

我们生活中的每一件普通和日常事物都以系统化的方式数字化,变得极其计算化、通信化、响应化和活跃。有了各种网络选项,各种嵌入式系统(从小到大)相互连接,以便有目的地被发现、访问、评估、互操作和协作。也就是说,所有构成数字化对象和连接设备之间以合理方式交流的异质性和复杂性,正通过软件赋能的抽象技术和技巧被立即消除。这是一个众所周知且无可争议的事实:软件正在吞噬世界。我们日常环境中的每一台设备、消费电子产品、工业机器、飞行无人机、类人机器人、医疗仪器、家居用品和器具、启用工具包、手持设备、可联网的可穿戴设备、便携式设备、固定系统、网络解决方案、汽车和引擎,都通过嵌入式软件适配器、连接器和驱动程序获得赋能。也就是说,软件赋能是使日常物体能够决定性地加入主流 IT 的最重要过程和目标。任何包含一些相关软件片段的东西都可以变得数字化重要。也就是说,普通事物正在为数字经济的下一个时代做好准备,变得非凡。高级汽车和车辆正在填充大量的软件模块,以便建立和确保高级自动化。

同样,家庭、建筑和工业自动化是通过嵌入软件库来实现的。飞行、无人机、机器人、SCADA 系统、传感器、执行器、工业机械、锅炉和油井,正通过软件代理的丰富来获得软件赋能。因此,软件赋能已成为原始设备制造商OEMs)的必修课。下一个问题是,如何使软件定义和驱动的系统找到彼此,以便启动富有成效的协作。API 已出现作为协助促进无缝和自发的设备集成的机制。

RESTful 服务和 API 是启用设备到设备D2D)和设备到云D2C)集成能力的主要和主导方法。网络物理系统CPS)的宏伟愿景是通过智能应用 RESTful 方法来滋养并繁荣发展。

数字智能时代的软件赋能云

如前所述,软件的角色和责任正在上升。软件是参与性的、普遍的,甚至是说服性的。所有商业机构都采用软件工程技术和工具,以实现更深层次和更果断的自动化。各个行业领域都热衷于制定和加强他们的软件组合,以便在竞争中脱颖而出。全球的软件开发者构建、编辑、精炼并将他们的软件应用存放在公开的软件注册表/仓库中,以促进其广泛使用。因此,随着软件模块的快速传播,软件包、产品和程序的应用正在稳步增长。应用领域也在不断扩展其视野。应用类型也在相应地扩展,以应对更复杂的需求,因为广泛的认识是软件决定了自动化之旅。在接下来的章节中,我们将讨论主要的应用类型。

物联网应用和服务

随着众多连接和集成技术的快速成熟和稳定,迷人的物联网时代已经开始展开,并为整个社会提供其独特的贡献。毫无疑问,物联网范式将为各种商业企业和组织带来各种创新、颠覆和变革。如果得到适当的、积极的利用,每个商业领域都将长期享受繁荣的物联网模式的独特好处。不仅企业,而且每个人、每个机构和创新者都将从日益增长的物联网力量中受益匪浅。根据全球市场研究和分析师团体的报告,物联网领域的短期和长期影响无疑是巨大的,具有趋势性。IT 学科,正被广泛认为是最大的商业推动者,随着物联网概念的到来和阐述,正获得巨大的推动。物联网理念正在迅速渗透,并变得普遍和有说服力。

随着物联网范式的持续传播和采用,商业和 IT 世界将受到众多高端物联网应用和服务的冲击。包括制造、零售、能源、医疗保健、智能城市、政府、国防、公用事业和物流在内的各个行业领域,都在精心探索和实验各种物联网技术和工具,以便在竞争中领先。各个行业部门通过展示一系列令人眼花缭乱的先锋物联网用例,以确保其忠诚消费者正确且相关。

物联网概念的第一个也是最重要的含义是数字化实体和元素(也被称为智能对象)的宏伟和大量实现。通过系统地利用边缘技术,各种日常物品正在转变为自我感知、周围环境感知和情境感知。从物联网领域的进步中产生的第二个值得注意的成果是连接设备的更快普及。各种嵌入式系统正在联网,并加入主流计算。第三个也是最终的结果是设备服务的增长,这些服务通常是微服务。随着微服务架构MSA)的更快采用,我们将被大量微服务淹没,以生产下一代企业、云、Web 和移动应用程序。预计到 2020 年,我们将有以下几点:

  • 数百万微服务

  • 十亿连接设备

  • 万亿数字化实体

我们拥有大规模数据存储和处理的庞大云中心。我们既有共享也有专用的网络基础设施,以便快速将数据传输到遥远的云环境。有持久和瞬态的存储选项。云存储容量的成本正在稳步下降。全球云中心的快速普及消除了在本地设置和维护大规模 IT 基础设施的担忧。有洞察力和集成的大数据、快速、流式和物联网数据分析平台。有许多使能框架、自动化工具和强大的引擎正在出现和演变,以加速数据转化为信息、知识,然后转化为智慧的过程。因此,几种经过验证和潜在的技术和工具的出现和融合简化并加快了数据分析和挖掘,以提取可操作的见解。简而言之,物联网设备的通信和协作产生了大量多结构化数据,这些数据必须被有意识地捕获、清洗和压缩,以便揭示隐藏的模式、有用的关联、更大的可能性、新的机会、可避免的风险和现实世界的智慧。所发现的知识将被传播到正确的系统、设备、应用程序、数据源和存储中,以便使它们能够确定其下一步的行动计划。

云启用应用程序

如前所述,在接下来的日子里,我们将探讨各种物联网应用和服务。此外,我们还将看到在云服务器上运行的、企业级、分布式应用。随着 MSA(微服务架构)的巨大成功,所有各种遗留(单体)应用正在有条不紊地被启用为云就绪和以微服务为中心的应用,这些应用以其可扩展性、可用性和灵活性而闻名。也就是说,大型应用正在被分割成多个细粒度、公开可发现、网络可访问、互操作、API 启用、可组合、可移植、水平可扩展和独立部署的微服务。有应用现代化和迁移工具包,可以将重构和修复后的应用迁移到云环境中,以获得云困境最初设想的所有好处。有大量最佳实践、集成平台和模式(架构、设计、集成、编排、安全性和部署),可以快速进行遗留现代化,以产生云启用应用。

云原生应用

新的应用正在被设计、开发、调试和直接部署在云环境中。在云环境中(私有、公共和混合)提供了生产、执行和编排平台。这些应用足够智能,能够累积云计算的所有好处。微服务是构建企业级和业务关键应用的基本和最佳构建块,这些应用将通过云环境交付。微服务被正式容器化,并且每天部署多次,以满足企业和客户不断变化的需求。有许多自动化工具消除了开发和运维团队之间的所有摩擦,以确保更快地将更新和升级的应用交付给用户和订阅者。

移动、手持和可穿戴应用

今天,数十亿部智能手机作为连接、访问和评估软件应用的主体设备,无论何时何地,无论何种网络,都能随时使用。也就是说,各种云、网页和企业应用都充满了移动界面。这种移动能力已经成为应用开发者和提供商的常态。不仅应用,软件和硬件基础设施也正在配备移动界面,以便进行远程监控、管理、诊断和维修。在我们的日常生活中,我们遇到数百万个在移动商店开发和存储的移动应用。智能手机用户可以轻松下载和安装它们。因此,如今,移动、手持和可穿戴应用都受到了用户和开发者的大量关注和喜爱。我们需要有能力的机制将这些应用的 API 连接起来,以便其他应用/服务可以轻松找到并绑定,从而推出以业务为中心的应用。有几种易于理解和使用的生成技术无关的 RESTful 服务和它们 API 的方法和机制。

事务性、操作性和分析型应用

企业应用在功能方面各不相同。它们不仅要很好地满足其功能需求,还应该满足许多非功能性需求NFRs),这些被称为服务质量QoS)和用户体验质量QoE)属性。性能、可扩展性、可持续性、可修改性、可扩展性、可用性、弹性、安全性、可靠性和适应性需求被归类为重要的 NFRs。至于公众,开放的互联网因其廉价而成为主要的通信基础设施,因为大多数应用都是网页化的。也就是说,网页界面是访问网页规模应用的广泛使用的机制。

企业对企业B2B)和企业对消费者B2C)应用在本质上通常是事务性的。有可行的技术来确保复杂的交易。随着地理分布系统的普及,分布式交易的需求变得更加重要。众所周知,网络是访问信息、内容、应用、服务和数据源的主导和决定性方式。因此,出现了许多在线事务处理OLTP)系统,以原生支持不同的交易需求。

就像交易一样,数据分析占据了下一代企业级系统的核心部分。数据被精心收集、清洗和计算,以便从不断增长的数据堆中提取可操作的见解。提取的见解被用于做出智能和实时的决策。精确地说,这些将是数据驱动的见解和见解驱动的决策。基于直觉的决策不再有立足之地。存在分析平台和数据仓库/集市/立方体,它们促进了数据挖掘、分析和调查。分析是任何商业应用、IT 平台和基础设施的核心,以提供它们独特的服务。所有下一代应用都天生具有分析能力。现有应用通过整合分析应用,如在线分析处理OLAP)应用,被赋予了分析能力。因此,分析应用将成为主流 IT 的主要组成部分。

最后,随着操作技术和信息技术环境的无缝链接,托管在企业服务器和云服务器上的软件应用必须能够接收实时操作数据和事件消息,以便对消费者来说是正确和相关的。各种操作应用和数据库正在出现,以满足不断变化的操作环境需求。地面操作系统产生大量的时序数据和事件数据,这些数据被流式传输到企业系统(事务性和分析性数据存储)。存在能够理解流入它们的数据的分析平台。更智能的环境,如智能酒店、家庭、医院、制造车间和云启用数据中心CeDCs),在它们与附近的或远处的交易性和分析系统无缝集成时,将获得赋能。

知识可视化应用

接下来的时代无疑是知识为中心的。知识发现和传播将成为知识工作者的主要活动。软件服务、个人和专业设备、IT 系统和商业应用必须提供实时和现实世界的知识,以便在行动和反应上具有适应性。有 360 度仪表盘、可视化平台和报告生成工具可供使用,以便图形化和直观地显示和传达结果。让我们更详细地看看这些应用。

社交应用

这套应用目前在年轻人中非常受欢迎。通常,Web 1.0 应用是简单且单向的,而 Web 2.0 应用则是社交性的,并促进双向沟通。也就是说,用户不仅阅读,还能回写。为了促进从外向内的思考,社交应用是前进的方向。有几个社交和专业应用能够赋予整个社会力量。数字社区正在形成并被用来装备人们的技能和知识分享。由于社交应用拥有大量的订阅者和追随者,它们产生了大量有用的数据。当社交和与人们相关的数据被持续收集并受到各种调查时,个人和机构必然会发现许多可操作的观点。由于社交应用的广泛传播,出现了新的分析能力,例如社交网络分析、行为分析和销售促销及营销活动分析。

科学和技术应用

软件在塑造各种科学和技术应用中扮演着至关重要的角色。科学实验会产生大量数据,这些数据可以被捕捉和加工以生成可用的结果。同样,也有利用软件能力的技术应用。这两个领域需要软件平台、产品、模式和流程的独特贡献,以便高度相关于其用户。有大量的针对数学的软件包来帮助数学家进行他们的研究活动;其他科学、技术、工程和艺术学科也从软件领域的进步和发展中获得了巨大的和无法衡量的益处。在软件部署和执行方面,信息和通信技术ICT)领域出现了无数的创新。

集中式和分布式应用

集中式和分布式计算模型之间存在着波动。云通常是由集中、整合和融合的环境组成,用于托管应用。最近,云已经实现了联邦化,以便托管和运行分布式应用。考虑到数据的指数增长和应用的复杂性,分布式计算的出现是无法阻止的。在云范式下,利用通用服务器进行大规模应用和数据处理的势头正在增强。水平可扩展性优于垂直可扩展性,因此分布式系统正受到越来越多的欢迎。应用也被有系统地分割,以便进行分布式处理。数据被分布到数千个工作/从节点上。计算移动到数据所在的地方。随着分布式计算的爆炸式增长,网络延迟问题也随之而来。随着分布式计算的接受和采用程度的提高,服务器虚拟化也扩展到了网络和存储虚拟化。MSA 的出现和阐述导致了分布式应用的实现。简而言之:分布式计算和应用是不可避免的。为了满足快速发展的商业和 IT 需求,它们必须被欢迎。

与集中式环境和应用相关的某些局限性。总体观点是,分布式系统确保了高可用性、可负担性和可扩展性。也就是说,未来 IT 的座右铭是分布式部署和集中式监控、测量和管理。

基于区块链技术的去中心化和智能应用

如前文所述,集中式应用非常适合某些场景。然而,在最近几年,去中心化应用的市场需求有所增加,这得益于区块链技术的快速采用和适应。区块链范式在实现和运行跨多个行业垂直领域的去中心化应用时,承诺带来一系列的颠覆和变革。集中式系统和应用面临的问题在去中心化服务和解决方案中得到了消除。通常,去中心化系统由不同的组织拥有和运营,因此去中心化软件的安全性在技术上得到了加强。通过去中心化方法,软件系统的所谓不可破和不可渗透的安全性得到了保证。去中心化方法促进的点对点P2P)交互正在成为许多近期用例的银弹。区块链技术的快速成熟和稳定性明显推动着 IT 专业人士和组织向去中心化系统的生产迈进。区块链范式还导致了智能合约这一新概念的出现,这导致了自适应应用的实现。

复合和多容器应用

分解和组合技术已被广泛用于在软件工程中取得突破。单体应用通过分解技巧被拆解,而分解的应用模块则以有序的方式相互组合,以创建更智能和更复杂的应用。随着容器成为微服务的最合适的运行环境,我们需要从容器化的微服务中生产出企业级、关键任务和自适应应用。有组合(编排和协奏)平台和引擎来简化并加快构建过程感知和以人为本的应用。还有使能语言,帮助我们开发过程优化和集成的应用。

事件驱动应用

我们仍然有许多单体和主机应用,尤其是在金融领域。如今,大多数应用都遵循客户端/服务器风格。有云(在线、按需和离场)应用。随着各种附加和第三方系统的加入,应用架构转向n层分布式计算。随着区块链的出现,P2P 架构模式得到了来自各利益相关者的广泛关注。还有其他变体,如面向服务的架构SOA)和面向资源的架构ROA),它们本质上支持请求/响应和触发即忘。此外,还有实现参与组件轻量级和松散耦合的方法。

但毫无疑问,未来属于事件驱动架构EDA),这是实现敏感和响应性(S 和 R)应用程序的前进方向。随着独立但相互连接的设备前所未有的爆炸性增长,EDA 风格促进了解耦应用程序。任何值得注意的事件或状态变化都会触发其他设备和应用程序采取行动。存在事件存储和处理平台(包括开源和商业级解决方案)。简单/原子事件积累和聚合形成复杂事件。事件消息被流式传输,以便进行各种调查,这些调查通过利用流分析解决方案和解决方案得到了极大的简化。存在用于事件和流处理的启用框架。应用程序被内在赋予根据从事件消息中提取的任何洞察进行适应的能力。应用程序将变得以人为中心,积极主动,同时提供其独特的服务。EDA 是构建智能系统的一种推荐方法。

高质量应用程序

正如讨论的那样,有大量针对 I/O 设备特定、服务器中心、语言导向、架构启发和技术无关的应用程序。今天,大多数应用程序都是被编码以满足已识别的功能需求。随着 IT 被宣布为更大的业务推动者,不断发展的业务需求要求 IT 专业人员和教授设计可行的方法将 NFRs(非功能性需求)纳入源代码。突出的 NFRs 包括性能、可扩展性、可用性、弹性、可靠性、安全性、可扩展性、可访问性和可修改性。这些 QoS(服务质量)和 QoE(用户体验质量)属性被要求优雅地嵌入到我们日常的软件应用程序中。一个新兴的新学科正在出现,并吸引了软件工程师和架构师的注意和喜爱——站点可靠性工程SRE);也就是说,不仅业务应用程序和敏捷生产的 IT 系统,而且它们还必须以高度可靠的方式进行设计、开发、调试、交付和部署。书面目标是确保应用程序的弹性和可靠的 IT 基础设施。IT 专家的未来挑战众多且多样化。构建高质量应用程序充满了无数困难。学者和科学家正在加班加点地提出最佳实践、知识指南、优化流程、架构和设计模式、集成平台、有能力的基础设施以及易于理解和使用的程序,以简化并简化高质量软件系统的生产。

弹性应用程序

如前所述,微服务被定位为构建和部署下一代应用的优选元素。不同的分布式微服务在组合后形成灵活的应用。随着对可靠 IT 系统和业务应用的广泛坚持,正在出现并迅速发展的可行方法,将所需的可靠性能力精确嵌入到软件系统中。一般来说,系统可靠性是应用的可恢复性和系统的弹性。也就是说,面对任何内部或外部攻击,应用必须生存下来,以便持续履行其义务。应用必须天生具备主动检测任何问题的能力,然后将其遏制,防止其级联到其他系统组件。因此,系统在技术上必须容错,以便具有高可用性。简而言之,恢复能力是识别和避免问题,而不会使整个系统崩溃。在满足用户请求时,为每个服务部署额外的实例非常方便。第二个方面是弹性特性;也就是说,当系统承受重负载时,它们必须相应地扩展或扩展,以应对用户和数据消息的额外涌入。因此,除了确保应用和数据的安全之外,保证可靠性正变得越来越重要。

应用现代化和集成的 REST 范式

单一和大规模的应用正在被现代化并迁移到云环境中,以便获得云计算模型最初设想的所有好处。微服务正成为生产企业级应用最优化构建块。不仅适用于开发,对于应用现代化,微服务也被誉为最合适的方法。也就是说,遗留应用正在被系统性地分割成多个互操作、可移植、公开可发现、网络可访问、可重用、可组合、细粒度、技术无关、容器化、水平可扩展和独立可部署的微服务。这里的要点是每个微服务都暴露一个或多个接口。RESTful 接口是微服务连接和组合更大、更好服务最受欢迎的接口。因此,微服务通常是 RESTful 服务。因此,通过 RESTful 服务和它们的 API,应用重构和修复正在加速和简化。服务、应用和数据集成以及编排通过 RESTful API 进行。

简而言之,新技术和工具包、编程和脚本语言、架构和设计模式、集成平台、开创性算法、使能框架、可组合和集群化的基础设施、优化流程、新的构建模块、数据格式和协议不断涌现并影响着软件工程学科。敏捷软件开发方法越来越受到重视,以快速构建应用程序。此外,微服务在实现企业级应用中的作用和责任越来越明显。也就是说,除了敏捷技术外,微服务对应用程序的快速开发做出了巨大贡献。换句话说,通过组合多个微服务,应用程序可以瞬间准备就绪。

应用程序主要相互依赖。它们不能独立工作。它们必须动态集成,为用户提供综合体验。应用程序还必须与其他应用程序、数据源和存储、数据处理和分析平台以及消息和中间件系统连接起来。因此,不可避免地需要通过精心设计和意图明确的 API 进行集成。最后,遗留应用程序必须分解成易于管理和松散耦合的模块。这些模块化组件与管理解决方案相结合,将显著提高其利用率、效率、可见性和可控性。进一步来说,根据需要,可以选择和组合几个模块来创建更大、更好的应用程序。

应用程序编程接口

我们正朝着一切即服务的方向发展。应用程序编程接口APIs)已成为企业表达和展示其服务能力的技术选择。每个服务都必须有一个或多个接口和后端实现。全球各地的公司都在积极采用 API。一些独特的使用模式已经出现,以帮助企业迅速适应不断变化的市场需求。那些正在制定数字化转型战略的企业正在加速利用多个渠道和 RESTful API 来超越竞争对手。API 已成为组织与业务伙伴、供应商、零售商、分销商、仓库提供商、物流和供应链专家以及消费者轻松连接的战略资产。这简单地说就是 API 经济。它们正在推动设计模式和用法模型,随着全球范围内的企业拥抱 API 的概念。

本节探讨了以下四种 API 使用模型,这些模型可以满足业务需求,并具有所需的敏捷性和效率:

  • 公共 API

  • 内部和私有 API

  • 用于物联网传感器和执行器的 API

  • 集成 API

用于外部集成和创新的公共 API

我们正被新一代和具有创新功能的 I/O 设备所包围。随着微型化技术的持续发展,我们得到了一系列轻薄、多功能、强大的智能手机、平板电脑、可穿戴设备、便携式设备和其他物联网设备的供应。也就是说,数字设备生态系统正在持续扩展。另一方面,快速积累的数字内容、信息和服务的可用性使得在任何时间、任何网络、任何设备上都可以找到、访问和消费。也就是说,有新的渠道可以连接到企业云服务器以获取和聚合各种服务,如信息、商业、交易、分析和其他在线服务。为了标准化服务发现、匹配和利用,广泛推荐的方法是利用 API。API 正在成为企业以受控方式共享其服务和信息的下一代渠道。随着数字生态系统的稳步增长,API 已成为跨国公司向更广泛市场提供其产品的前进之路。随着数字资产的爆炸性增长,API 正在成为访问、评估和使用数字资产的主要方法。数字服务提供商的数量激增。为了创造更高的商业价值,地理上分布的服务提供商需要通过 API 按需识别和集成。每个服务都配备了一个或多个 API。开源社区提供了 API 管理和网关解决方案,作为商业级解决方案提供商,以降低 API 开发、运营和管理复杂性。

用于内部目的的私有 API

内部 API 提高了企业内部以及跨企业的利用率和效率。内部 API 使得内部开发者能够以自由流动的方式轻松发现和消费内部服务。新兴的趋势是每个有价值的应用程序都细致地被服务和 API 启用。通常,每个企业都配备有各种后端系统,如数据库管理系统、面向消息的中间件MoM)解决方案、消息代理和队列、数据处理和数据分析系统以及知识可视化工具,这些系统都启用了服务以暴露它们自己的接口(API),以促进服务连接、集成和编排的目标。当组织想要为内部使用创建新的 API 时,它必须将它们添加到现有系统的服务 API 之上。

公共 API 并不多,但内部使用的 API 却很多,因为可能有数百个内部服务利用多种数据格式和传输协议。为了大力促进重用、速度、效率和敏捷的应用程序开发,企业应将其内部 API 发布在可搜索的目录中。

物联网设备 API

我们在本章开头讨论了物联网设备及其对人类的服务。预计未来几年将有 500 亿个连接的设备。这些一次性使用但不可或缺的设备正在大量生产,以自动化日常活动。嵌入式设备通过网络相互连接,以协作方式共同开发并向人们提供情境感知服务。正在挖掘和推出新的用例,以增强物联网范式的普及和渗透。由于物联网设备的多样性和异质性,物联网设备的运营、管理和安全复杂性正在部署到家庭、酒店、医院、零售店、制造车间、火车站、餐厅和自动驾驶汽车中。如今,传感器和执行器已成为每个数字应用的眼睛和耳朵。每个设备都变得可计算、可通信、可感知和活跃。

存在边缘和雾设备,它们具有足够的功率形成临时的云,以捕获、存储、处理和分析实时、时序和流数据,以提取可操作见解,这些见解可以反馈到设备和人员,以便及时做出智能决策,并参与正确和相关的活动,具有清晰和自信。未来的物联网应用和服务需要通过 RESTful API 公开,以执行设备集成和服务编排。

应用集成 API

API 注定将在满足过程、数据和应用程序集成复杂任务中发挥至关重要的作用。SOA 为面向服务的应用程序和集成提供了一个刺激的平台。企业服务总线(ESB)产品的快速成熟和稳定性加快了面向服务、集成和洞察驱动的企业的建立和维护。对生产集成系统进行解耦服务的 API 驱动的集成引起了广泛关注。

简而言之,全球企业正在拥抱 API 作为实现备受瞩目的数字颠覆、创新和转型的战略途径。外部 API 为客户端开发者实施和参与公开的 API 提供了连接和协作的软件应用。通过外部公开的 API,可以实现应用程序的远程监控、测量和管理。

描述 RESTful 服务范式

软件工程领域的开发趋势是,现在应用程序是通过集成、编排和协作由多个服务构成的。工作流是突出的统一因素。也就是说,它们有助于识别相关的服务,所选的服务被整合到适当的顺序中,以形成有能力的复合服务和应用程序。挑战在于服务发现、访问、评估和使用。有通信和数据传输协议来促进服务之间的交互和协作。REST 方法正变得易于理解和使用。本节将描述 REST 是如何简化的,以及交互式服务如何导致强大的应用程序。

资源,作为 ROA(资源导向架构)的主要架构组件,类似于流行于面向对象编程OOP)概念的物体。作为物体,资源有直接作用于其上的方法。OOP 和 ROA 之间的关键区别是,资源只定义了少数几个标准方法。然而,对于物体,可能有一个或多个方法。进一步来说,ROA 中的资源可以被合并成资源集合。条件是每个创建的此类集合必须是同质的。资源可以赋予数据。与资源相关联的数据的丰富性有助于以高效和有效的方式利用资源。JSON 是最合适且最受欢迎的数据模型,可以适当地丰富资源。资源越来越多地使用 JSON 对象来表示。特殊的关键字值对_type被广泛用于存储任何资源的类型。JSON 数据类型,如字符串、数字、布尔值、null 或数组,是任何关键字值对的值。

REST 范式包括三个突出的架构元素类别:连接器、组件和数据元素。

连接器是一种将你的参考点连接到目标系统的事物。连接器负责识别和访问各种网络资源,并更改已识别资源的当前表示。角色是各种组件的接口,这些组件由不同的编程语言实现:

  • 客户端连接器:REST 是用于请求-响应范式的。客户端发送请求并从资源获得适当的响应。

  • 服务器连接器:RESTful 服务器持续监听来自客户端的请求。服务器为请求提供响应。

  • 可缓存响应:可以存储在客户端或服务器上,以加快后续请求的响应速度。多个客户端可以同时获取缓存信息。

  • 解析器:将资源标识符转换为网络地址。

  • 隧道:中继服务请求。有时,组件会偏离其基本义务进行隧道操作。

在 REST 范式中,相互交互的各种软件被称为组件

  • 服务器软件:此软件解决方案使用服务器连接器从客户端软件接收请求。服务器是资源及其表示的来源。

  • 客户端软件:客户端使用客户端连接器制定并向服务器传达请求,并接收响应。

  • 网关软件:这是一种中间件解决方案,能够智能地翻译客户端和服务器之间发生的请求和响应。

数据元素是强大的 RESTful 服务范式的关键组成部分。之前提到的 REST 组件传达数据元素的状态表示。在 REST 范式中,有六个数据元素:

  • 资源:根据 REST 规范,ROA 是核心的架构风格和模式。资源是 RESTful 范式的核心元素。资源是对各种物理或逻辑/数字/网络/虚拟实体的概念映射。映射会随时间变化。每个 RESTful 资源都通过适当的地址唯一表示和标识。资源包括 Instagram 上的图片、电影标题等。

  • 资源标识符:为了找到、访问和使用资源,资源需要唯一标识。统一资源标识符URI)标识每个资源。URI 是客户端和服务器以明确方式通信的方式。资源可以有多个 URI。这成为指明资源不同位置细节的必要条件。URI 用于交换资源表示。

  • 资源元数据:元数据对于资源利用和管理非常重要;它提供了关于资源的额外细节。添加的信息,如位置信息和资源的替代标识符,使得资源操作和管理成为可能。也就是说,资源还包括 RESTful API 特定的信息,如 URL 和关系。

  • 表示形式:我们讨论了资源并指出 JSON 是定义与资源相关数据的首选数据模型。然而,为了资源能够通过 HTTP 连接与客户端通信,它们的表示形式必须转换为文本表示。这种表示形式必须正式嵌入到 HTTP 消息体中的实体。确切地说,资源表示形式是资源在特定时间点的状态,并且会发生变化。状态值在客户端和服务器之间传输。表示形式通常捕捉并传达资源的当前或期望状态。特定的资源可以有多个表示形式。

  • 表示元数据:这些元数据提供了关于表示形式的额外细节,以便简化表示。

  • 控制数据:这定义了对资源请求的操作。

REST 架构约束

REST 架构约束主要是设计规则,这些规则清楚地传达了 REST 范式的独特特征。这些约束并不是为了规定使用什么样的技术和工具;它们只是表明数据如何在各种组件之间传输,例如客户端和服务器:

  • 客户端和服务器之间的关注点分离:客户端和服务器之间存在明显的分离。这种独特的分离使得客户端-服务器应用程序可以独立开发和部署。任何一项的进步和变更都不会影响另一项的功能。解耦的特性保证了消除依赖性引起的问题。

  • 无状态通信:服务器机器不需要存储任何关于客户端调用上下文的会话信息。这意味着任何新的请求都可以由任何服务器实例处理。然而,会话中客户端的认证细节可以被存储,以便会话内的新请求不需要通过身份和访问管理系统进行认证。

  • 客户端缓存响应:客户端可以缓存服务器响应,因为每个服务器响应都包含启用缓存的相关细节。

  • 连接可以是直接的或间接的:客户端可以直接与服务器通信,也可以通过中间件(如代理或其他经纪人)进行通信。这种分离增加了系统的灵活性。通过这种中间件,可以轻松满足可扩展性的需求。

  • 统一接口:由于各种网络应用组件(客户端、服务器和中间件)接口的统一性,它们之间的交互得到了简化。如果任何组件偏离了既定的标准,那么网络应用可能会崩溃。四个基本的 HTTP 操作,GETPOSTPUTDELETE,为所有参与组件提供了所需的统一性,以便清晰、自信地查找、交互和完成任务。其他操作,如HEADOPTIONS,主要处理元数据管理。

  • 分层系统:分层和分层系统减轻了开发和运营的复杂性。越来越多地,客户端和服务器之间有许多层。这些层充当中间件,如网关和代理,以自动化交互的一些方面。代理通常是由客户端选择的中间件。代理提供接口以访问服务,如数据转换、性能增强和安全保护。另一方面,网关是由网络或服务器强加的另一个中间件,以提供特定服务的接口。

利用中介组件可以显著减少交互延迟、安全执行和旧系统的封装。因此,为了更快地开发 Web 应用程序,并使旧应用程序更加现代化、模块化和 Web 化,RESTful 服务范式做出了巨大贡献。

简单对象访问协议(SOAP)机制是访问和使用服务的原始方法。它是一种基于 XML 的消息协议,用于在计算机之间交换信息。SOAP 存在一些持续的问题,因此 REST 范式的起源和传播受到了广泛的欢迎。由于 Web 应用和服务数量的激增,创建和维护高效的 Web 架构已成为一项重要任务。

REST 方法是一种著名的架构风格,与无处不在的 HTTP 协议中使用的概念紧密相关。REST 不规定组件实现和协议语法的细节。然而,它包括对连接器、组件和数据的根本约束。这概述了 Web 架构的基础,以及作为基于网络的程序的行为本质。REST 范式通过一系列架构约束说明了简单的 Web 应用程序设计和开发。这些架构考虑确保了组件交互的可伸缩性、标准接口和组件的独立部署。

精确地说,REST 本身并没有定义各种资源的状态应该存储在哪里或如何存储。REST 指定了如何使用无处不在的GET操作来检索状态。状态也可以通过PUTPOST操作提供。还可以有只支持GET方法的只读资源。正如在其他地方讨论的那样,资源状态可以通过任何方式提供,例如文件系统、其他资源的动态组合或物理传感器。REST 范式是设计面向资源的应用程序服务的方法或设计模式。也就是说,REST 是网络应用程序的架构风格。

REST 提高了基于网络的程序的性能、可伸缩性、简单性和可见性。REST 自然鼓励正确和高效的开发人员与计算机以及开发人员之间的沟通。因此,REST 可以描述为通过遵循消费者和提供者之间的一系列特定约束来构建服务和应用程序(我们在本章开头描述了各种软件应用程序)的方法。

REST 的优势

通常,在请求和提供服务之间可能会有许多交互来满足业务流程,因此,不同和分布式微服务之间的通信必须快速进行,并且开销要小。REST 要求具有较低延迟的网络以加快服务请求和满足,这使得 REST API 非常适合。REST API 不仅支持 XML 和 JSON,还支持更优化的二进制表示格式,如协议缓冲区或 Avro。进一步来说,它可以升级到 HTTP/2.0。全球数十亿人使用网络进行各种目的。有数百万的开发者创建各种网络应用程序。因此,需要一个有能力的网络架构来满足企业和人们的不同需求。简而言之,强大的 REST 范式有助于构建软件架构和应用程序,这些架构和应用程序隐含地继承了网络的所有值得称赞的品质。RESTful 服务带来了一些重要的能力,如更高的可伸缩性、高效的网络使用以及客户端和服务器独立运行。

REST 范式本身支持 ROA。根据 REST 规范,一切都是资源。软件架构通常是几个可配置架构元素的组合。在 REST 的情况下,主要的架构元素是组件、连接器、资源、表示和一些数据元素。资源是 RESTful API 的构建块。资源可以是应用程序想要在网络上公开的任何东西,以便其他应用程序可以通过各种 HTTP 方法找到并操作。它可以是有文本内容、图像、视频和音频、白皮书或银行账户。这些资源通过在 HTML 文档中嵌入相应的超链接相互链接。请注意,资源可以通过人类和软件程序检索、更新和删除。以下图示说明了这一点,并显示了通过超链接在不同和分布式资源之间的潜在关系:

图片

为了明确地找到并使用它们,每个资源都必须有一个唯一的名称,这被称为 URI。一个示例 URI 是www.paris.fr/weather。如前图所示,资源可以通过表示的概念来暴露其状态。表示通常包含元数据(如资源大小、媒体类型或字符集)和内容(二进制图像或文本文档)。表示差异很大。例如,电子商务网站上购买确认的表示可能是一个 HTML 文档。对于婚礼照片,表示可能是一个 JPEG 图像流。对于地址簿网络服务中的联系人,它可能是一个 XML 文件。因此,它从资源标识开始,并为每个标识的资源有一个相应的表示。

自描述消息

客户应通过一系列请求消息表达他们偏好的状态。服务器通过响应消息将资源的当前状态传达给任何客户端。例如,一个维基页面编辑器可以向服务器发送消息,请求转移表示。表示的变化可能表明页面更新,这是服务器托管和管理的网页的新状态。然而,服务器负责接受或拒绝客户端的请求。如前一段所述,自描述消息可能包括元数据来携带和传达有关资源状态、表示格式和大小以及消息本身的额外细节:

图片

在资源当前状态下执行的核心操作(GETPUTDELETEPOST)在以下图中以图形方式表示。状态通过这些操作进行修改和更新。该图还表明,资源与其外部环境是分开的,它与之交互。也就是说,多个实体可以无问题地与资源交互:

图片

资源以某种状态启动,该状态位于前一个图的中间。状态以任何合理的方式进行管理。从数据库或文件中写入或读取是一回事,但没有任何后端数据库,业务逻辑无法进行任何动态计算。结果被发送回请求的客户端。前面提到的核心操作定义了其统一的接口。

建立并提供 REST API 并非易事。根据 REST 范式,一个应用程序由多个分布式资源组成,这些资源为应用程序的消费者提供有用的功能。API 开发者必须理解问题域,分析业务、技术和用户需求,并据此设计各种参与和贡献的资源。资源选择最终导致 API 的形成,这些 API 需要被发现和使用以实现业务自动化。以下图示说明了我们如何将 REST API 定义为一系列超链接资源。这些资源由网络服务、网站和微服务公开:

图片

我们正朝着软件定义的世界迈进,每个有形的事物都通过适当的 API 得到启用。因此,备受赞誉的 API 经济开始闪耀。企业热衷于拥抱这个战略上明智的转型。API 已成为我们业务和 IT 系统的有趣和鼓舞人心的组成部分:

图片

具体来说,由 REST 范式提供的 ROA 正在被公认为实现预期数字化转型的主要贡献者,不仅对商业企业、政府机构和 IT 公司,而且对整个人类也是如此。随着技术的渗透到日常生活,人们在决策和行为上将会变得最聪明。SOAP 是服务启用目标的第一种行业级方法。由于各种复杂性因素,REST 正在获得市场份额和心智份额,如以下章节所示。

SOAP 与 REST 的比较

SOAP 是一种成熟且稳定的协议,具有多个标准规范,这些规范正在简化并简化服务的开发、部署、管理、治理和组合。有标准标记语言来表示服务的接口。为了明确理解,SOAP 主要使用 XML 而不是 HTTP 来定义消息内容。"Web 服务描述语言"(WSDL)可以强制服务 API 和消费者之间使用正式合同。SOAP 内置了 WS-reliable messaging 标准,以在异步执行和处理期间增加服务安全性。SOAP 内置了有状态的操作能力,用于会话状态管理。

如前所述,REST 易于理解,因为它使用 HTTP 作为数据传输协议和基本的 CRUD 操作。这种易用性简化了软件开发者的工作。REST 消耗的网络带宽也更少,因为它不像 SOAP 那样冗长/庞大。与 SOAP 不同,REST 被设计为无状态的,并且 REST 响应可以在客户端缓存,以保证更好的性能和可扩展性。REST 本质上支持许多数据格式。最广泛使用的数据格式是 JSON,它能够为 Web 浏览器和迷你客户端提供更好的支持。JSON 与 JavaScript 的关联简化了 API 负载的消耗。由于轻量级特性,RESTful 服务正在变得无处不在。各种云、移动、嵌入式和物联网应用程序都在利用 REST 范式,这对于应用、数据和 UI 集成需求越来越重要。

何时使用 REST 与 SOAP

在许多方面,REST 优于 SOAP,包括以下方面:

  • 开发公共 API:以资源或数据为基础的 REST,以其 ROA 而闻名,不可避免地专注于基于资源或数据操作。其核心操作(GETPUTPOSTDELETE)继承自无处不在的 HTTP。这种最小化方法使得 REST 范式易于理解和在应用和行业领域中使用。应用程序和服务开发者发现使用 RESTful 方法很容易。响应很容易被网络浏览器消费。确切地说,REST 固有的简单性是其前所未有的支持和成功的关键原因之一。意识到其潜力,从 SOAP 到 REST 的转变是一致的。

  • 性能、灵活性和可扩展性:API 是应用程序被识别、集成和使用的入口点。应用程序的 API 之间存在事件消息和过程/方法调用。一个广泛流传的建议是,需要大量双向消息的应用程序必须选择 REST 方式才能成功。如果出现网络问题,一旦重新建立连接,RESTful 服务方法允许应用程序/进程重试。REST 使得这样做而不会造成任何重大中断变得容易。与 SOAP 的状态操作相比,重试方面似乎是一个困难的事情,因为它涉及到多个级别的更多初始化,包括状态代码。由于 REST 是无状态的,会话信息不会存储在服务器机器上,这使得 REST 服务可以独立重试并且横向可扩展。

RESTful 服务范式使我们能够轻松快速地对 URL 进行调用并获得即时响应。SOAP 服务需要与一个复杂的客户端保持状态连接。另一方面,REST 支持无状态连接。缓存不会存储在服务器应用程序中。因此,与 SOAP 应用程序相比,测试 RESTful 应用程序相当容易。

SOAP 提供了通过过程/方法请求远程访问和操作对象(名词)的方法;REST 则侧重于可以在资源上执行的操作(动词)。因此,REST 已被公共 API 实践者广泛采用。在不需要将一组对象完全映射到客户端的情况下,REST 总是比 SOAP 更好。来回传输对象细节会浪费大量的昂贵网络带宽。此外,网络延迟也会出现。特别是在带宽受限的环境中,这种多次调用需求可能是一个巨大的障碍。主要被移动应用程序消耗的 API 与那些我们根本不需要利用 SOAP 的场景相关。公共 API 经常变化,因为消费者和企业的期望各不相同。在这个初创公司和没有具体合同协议的 API 的世界里,REST 是一个自然且通用的选择。

基于 REST 的微服务的最佳实践

在本节中,我们将讨论一些最佳实践,这些实践可以使您的 MSA(微服务架构)对开发者友好,从而他们可以轻松地管理和跟踪错误:

  • 有意义的名称:在请求头中提供有意义的名称始终很重要,这样如果发生任何问题,例如性能下降、内存浪费或用户负载激增,开发人员和性能工程师可以轻松地了解这个请求是从哪个微服务发起并级联的。因此,在请求头的User-Agent属性中提供逻辑name/{服务 ID}是一个最佳实践,例如,User-Agent:EmployeeSearchService

  • API 管理:在基于 REST 的微服务架构中,一个微服务通过 API 访问另一个微服务。API 充当其他微服务的门面。因此,必须谨慎构建 API,更改 API 会带来额外的问题。也就是说,API 必须考虑到未来的需求来设计。API 方法签名中的任何更改都不好,因为许多微服务依赖于 API 来访问和评估微服务。因此,在日益以 API 为中心的世界中,API 使用、版本管理和管理的任务具有特殊的重要性。

  • 关联 ID:为了确保高可用性,微服务通常分布在多个服务器上。也就是说,可能会有多个相同的微服务实例。随着容器成为微服务最优化运行时的出现,运行多个微服务实例已成为新常态。为了满足一个客户端请求,控制必须通过多个微服务和实例。如果某个服务在管道中由于问题表现不佳,我们需要了解该服务的真实情况以确定我们的行动方案。在连接和云时代,服务跟踪和分布式跟踪的方面对于微服务架构的成功和智能变得尤为重要。广泛推荐的做法是为每个客户端请求生成一个随机的 UUID,并将其传递给每个内部服务请求。然后,通过捕获日志文件,服务操作员可以轻松地确定有问题的服务。

  • ELK 实现:微服务小而简单。在任何 IT 环境中,可能有数百个微服务,每个微服务都有多个冗余实例以确保所需的容错性。每个实例都会生成一个日志文件,管理员发现访问每个日志文件以查找有用的信息并不容易。因此,捕获和存储日志文件,在日志文件存储上实施强大的搜索引擎,并将适当的机器学习ML)算法应用于这些日志数据,以提取和发出任何有用的模式、值得注意的信息或有益的关联,对于理解日志数据至关重要。ELK,这是一个开源软件,以紧密集成的方式满足这些不同的需求。E 代表 Elasticsearch,L 代表 Logstash,K 代表 Kibana。Elasticsearch 只是将日志存储起来并提供模糊搜索功能,Logstash 用于从不同来源收集日志并进行转换,而 Kibana 是一个图形用户界面GUI),它帮助数据科学家、测试人员、开发人员甚至商业人士根据其不断变化的需求有洞察力地搜索日志。考虑到日志分析的重要性,有开源以及商业级解决方案可以从微服务交互日志数据中提取日志、操作、性能、可扩展性和安全洞察。

  • 弹性实现:有一些框架和解决方案在服务相互交互时保证了可靠性(弹性+伸缩性)。

基于 REST 的微服务不仅因其极端的简单性而受到欢迎,还因为服务通过 HTTP 直接(同步)相互通信。这种直接通信意味着不需要任何中间件,如中心、总线、代理或网关。例如,考虑一个 B2C 电子商务系统,当特定产品重新入库时,它会立即通知客户。这种通知可以通过 RESTful 微服务实现:

应当注意的是,通信是点对点的。尽管如此,硬编码服务的地址并不是一个好的做法。因此,一个显著的解决方案是利用服务发现机制,例如 Eureka 或 Consul。这些是高度可用的集中式服务器,服务在这里注册它们的 API 地址。服务的可用状态被注册在集中式服务器上,以便进行即时服务。客户端服务可以从这个集中式服务器请求特定的 API 地址,以便识别和利用适当的服务。然而,还存在一些缺点,如下列所示:

  • 阻塞:由于 REST 方法同步的特性,更新库存操作不会做任何事情,直到通知服务完成通知所有相关客户的任务。如果有成千上万的客户希望被通知额外的库存,系统的性能必然会急剧下降。这种性能问题是由于紧密耦合的方法造成的。克服这些问题的方法之一是采用管道模式。架构图随后被修改如下:

图片

在这里,通信仍然是基于 REST 的,但真正的转变是点对点通信被永久消除。管道实体完全负责协调控制和数据流。服务是完全解耦的,这种解耦使微服务具有自主性。然而,采用这种方法,服务必须依赖于管道编排来贡献其作用,因此服务是自我定义的,但不是自给自足的。

  • 异步消息:考虑一个典型的基于消息的系统。在这里,输入和输出服务都可以定义为命令或事件。每个服务都订阅它感兴趣消费的事件。此外,当其他服务将事件放入队列时,这些事件可以通过机制(如消息队列/代理)可靠地接收。采用这种方法,库存通知子系统现在可以被重新设计如下:

图片

这种改进的架构带来了一系列关键优势,如增强的灵活性、服务隔离和自主性。这种转变简化了服务的添加、删除或修改,而不会影响其他服务的操作或代码。任何类型的服务故障都可以优雅地处理。在设计和发展基于微服务的企业应用程序时,这些都需要仔细考虑。

随着技术的日益复杂,通过各种实验得出的最佳实践和程序对架构师和开发者来说非常有用,他们可以创建战略上合理的软件系统。随着微服务成为生产级和可扩展的商业和 IT 系统的最佳构建块,我们的关注点转向了利用成熟的和稳定的 REST 范式来创建和维持业务关键和以微服务为中心的软件应用程序的方法。

API 优先的方法

我们将拥有令人眼花缭乱的物联网设备、数据、服务和应用程序。由于物联网传感器和执行器的无限制爆炸式增长,数据分析将占据更大的比例,这使得任何物理、机械和电气系统都可以数字化和连接。此外,Web、企业级和云应用都实现了移动化。因此,有多个渠道用于应用程序、设备和信息的访问和使用。企业移动性领域非常受欢迎。存在对性能有强烈需求的传统应用程序。应用程序拆解需要大量的时间和人才。因此,流行的做法是为现有应用程序附加额外的 API,以便使其对外部世界可用。然而,这并不是一个战略上明智的方法。另一方面,考虑到云基础设施的优化和组织性质,新一代应用程序正在直接设计、开发和部署在云服务器上。

因此,推荐的方法是首先构建 API,然后在上面构建云应用。广泛流传的 API-first 开发策略(www.restcase.com/)背后的理念是达到一个未来派的 API。这种方法使软件开发者能够以清晰和自信的方式完成他们的工作。实现可以是高度先进和高效的。这种接口和实现的分离使得在以后的时间点可以轻松地整合修改后的代码,而不会影响对应用程序和服务的访问。

将云定位为所有软件系统的一站式 IT 解决方案,单体和大规模应用程序正在实现云化,以便开放创新、颠覆和转型。绿色 field 应用程序主要作为云原生应用程序启动和实施。存在代码仓库(公共和私有)。云原生代码一旦完成,就被检查到仓库中。因此,代码被构建并与所有必要的代码段集成,以创建一个集成应用程序。对应用程序进行关键测试,以检查其代码是否成功通过所有关卡。然后,还有持续交付和部署工具,将精心挑选和精炼的代码交付到预配的 IT 资源,以启动部署过程。

开发 API-first

这是一个有趣的策略。开发工作正在按顺序进行。需求收集、服务设计、开发、调试、交付、部署和退役通常同步进行。质量控制和保证团队正在等待代码完全开发和集成。一旦原型准备就绪,文档团队开始准备 API。如果出现任何改进、纠正或添加需求,设计和开发团队开始着手处理。这是人才和时间的一种浪费。如果与项目赞助者和最终用户协商确定拟议服务的 API,则软件构建和部署时间将大幅减少。这将在以下图中展示:

图片

因此,推荐采用 API 优先的开发流程,因为它促进了所有团队的并行开发。软件可以独立发布。在这种情况下,对其他团队的依赖大大减少:

图片

在这里,首先创建并模拟了 API。然后后端、前端和测试团队开始使用模拟 API 进行工作。一旦API最终确定,所有团队都可以轻松切换到生产或预发布 API。这个程序节省了大量开发时间。

RestCase是一个基于云的 API 开发平台。该平台允许开发者使用直观的基于浏览器的界面协作创建 REST API,该界面自动生成文档、测试和模拟。RestCase 通过创建开发者可以立即对其发起调用的 API 模拟,从而实现快速迭代和测试,无需等待 API 的实际开发和部署,从而消除了来自各个开发团队的障碍。

首先构建服务 API

这是一种专家们明智且广泛推荐的最佳实践。通过首先定义和设计 API,可以正确地完成各种事情。项目赞助者可以全面了解正在开发的系统。在这种情况下,合并更改相当容易。软件开发者可以自信地继续他们的开发和测试任务。销售人员可以向任何潜在客户和消费者解释软件系统的细节。可以先进行 API 测试。在日益增长的 API 世界中,API 管理变得简单。为了满足不断变化的需求,一切都被配备了一个或多个 API 以便被发现、绑定和使用。

摘要

在本章中,我们讨论了 RESTful 服务和 API 在使数字化转型的企业和社会道路更容易导航方面的显著特点。REST 范式的简洁性和模块化导致产生了高度灵活和未来派的软件应用。随着数字资产的网络和云赋能扮演越来越重要的角色,REST 理念的角色和责任在未来必将增加。我们倾向于深入连接我们的日常环境。拥有众多认知环境的梦想持续上升。因此,显然,迷人的 RESTful 服务的独特和内在能力应该被充分利用,以实现本章所描述的事情。本章为您提供了大量关于 REST 概念及其如何在 IT 世界中成为趋势的信息。本书将涵盖 RESTful 范式的各个方面。

进一步阅读

第十章:框架、标准语言和工具包

软件框架是软件应用的命脉。它们提供了扩展的功能,并提供了许多开箱即用的实现,这样应用开发者就不需要处理软件的每一个编码方面,而是可以通过使用框架提供的开箱即用功能、库、API 和模型来专注于构建更快、更智能的业务能力。

我们将简要介绍一些流行的框架,并附带一些关于它们支持的编程语言、功能、标准以及特性的信息,例如足迹、适应性、云部署友好性和开发易用性。

本章的目的是向读者介绍一些在为他们的 API 开发需求选择正确框架时可能会派上用场的突出框架。请注意,所讨论的框架列表并不全面,也没有意图提供它们之间的比较。

以下章节目标:

  • 本章的目的是向希望使用熟悉的编程语言快速启动他们的 RESTful API 和微服务的应用开发者介绍一些突出的框架。

  • 这是一种尝试为读者提供介绍、指南以及一些对编程语言友好的框架的优缺点,以便他们可以根据自己的 RESTful API 开发需求选择并使用更合适的框架。

技术要求

由于我们将讨论基于 Java、Python 和 Go(编程语言)的几个突出框架,对一种或多种编程语言的基本理解将使本章的读者能够快速启动他们的 RESTful API 开发,并使用他们喜欢的框架之一。本章既作为参考资料,也作为对这三种编程语言中任何一种有基本理解的人的技术指南。

框架的核心特性

如我们所知,框架是软件库、API、脚手架、AJAX、缓存、安全、编译器等等。我们必须通过以下任何框架的核心品质来刷新我们的记忆,因为我们的框架选择依赖于这些品质:

  • 简单、一致、易于适应,且易于实现

  • 分层架构,设计良好且文档齐全

  • 做出真正的权衡

  • 使用可重用库和重用库(借鉴自过去)

  • 集成和设计以适应变化

让我们看看几个基于 Java 的框架,以了解它们在设计、足迹、文档和适应性方面的能力,以及它们的优缺点。

Spring Boot

最受欢迎的开源 Java 框架之一是Spring Boot。它为许多 Java 开发者提供了一个优秀的平台,可以快速构建和部署基于 REST 的应用程序。

Spring Boot 的基本设计原则如下:

  • 为所有 Spring 开发提供快速且易于访问的代码、可重用库和模板

  • 有自己的见解(确定、强烈和表达),跳出思维定式,并为开发者提供根据其需求定制的途径,并挑战默认设置

  • 提供非功能性需求特征变化,这些变化适用于项目的类,可用于仪表化(例如安全、嵌入式服务器、健康检查、指标和外部化配置)

  • 没有代码生成机制,也不需要 XML 配置

模板代码或模板表示在软件应用程序中集成的代码或代码库,我们可以通过少量或无修改地重用这些库。

让我们看看是什么让 Spring 框架成为应用开发者进行 RESTful API 开发的热门选择。

Spring 的核心功能

Spring 的应用程序、外部配置、配置文件和日志记录是 Spring 核心功能的组成部分。让我们看看每个组成部分及其值:

  • Spring 应用程序提供了一种方便的方式来启动我们的应用程序

  • 外部配置帮助我们使用 YAML、环境变量或命令行参数在不同的环境中使用相同的应用程序代码

  • 配置文件将应用程序配置的部分隔离开来,并使其仅对某些环境可用

  • 它提供了开箱即用的 Apache Commons Logging 功能,然而,这并不会阻止我们使用不同的日志框架

使用 Spring 数据进行数据库集成

数据库集成是任何生产级软件应用程序的重要组成部分,我们将观察 Spring 如何通过提供对传统 SQL 数据库和 NoSQL 技术的集成能力来使开发者的生活变得更美好:

  • SQL:Spring 为与 SQL 数据库一起工作提供了广泛的支持。使用 Spring 数据的 JdbcTemplate 进行 ORM,提供了称为 仓库创建 的额外功能层。

  • NO-SQL:一个基于 Spring 的数据访问编程模型,称为 Spring 数据,为 Spring 框架提供动力,并提供了一种快速便捷的连接机制,用于连接各种 NoSQL 技术。

消息集成

使用 Spring 消息框架,与消息系统的集成得到了极大的简化。无论是使用 JmsTemplate 进行简单消息传递或 ActiveMQ 支持,还是使用 AMQP 进行高级消息传递或 Apache Kafka 集成,Spring 框架都提供了简单的方法来实现消息集成。

通过自动配置扩展 Spring

在许多实际场景中,我们需要开发共享库(在组织内部或作为对开源的贡献)和在这种情况下创建特定的配置类——作为模块(JAR)。将其放在应用程序的类路径中,通过消除在自动配置类中定义特定 bean 的需要,可以加快并简化开发。配置示例可以是 LDAP、不同的数据库源配置或安全配置。

编写单元测试和集成测试用例

编写单元测试和集成测试是任何开发者的基本实践。Spring 提供了更好的能力来编写单元测试、隔离脚本中的测试以及集成测试。Spring 框架提供了一些测试应用程序的实用工具和注解。对于大多数开发者来说,Spring-boot-starter-test 是首选的测试实用工具,因为它的入门工具导入了 JUnit、AssertJ、Hamcrest 和 Mockito。Spring-test 和 Spring-boot-test 是用于集成测试的常见库,在编写集成测试时非常有用。令人兴奋的是,Spring 测试框架允许我们添加一些自己的额外测试依赖项。

Spring Boot 的好处

在我们转向另一个流行的框架之前,让我们看看 Spring Boot 的一些优点:

  • 快速设置、快速开发和推送到生产(企业就绪)

  • 与安全、ORM 和 JDBC 轻松快速集成

  • 内置轻量级 HTTP 服务器

  • 除了 Java 之外,它还支持 Groovy

  • 支持 Maven 和 Gradle 构建工具

  • 模块化,与其他库兼容良好

  • 学习快速、文档广泛且深入

  • 在线和离线都有非常活跃的社区(开发和文档)

Spring Boot 的缺点

虽然 Spring 有许多优点,但我们也应该意识到它的缺点:

  • 版本之间的频繁破坏性更改(通过引入新功能和缺陷修复)

  • 可能会强制使用最新版本

  • 过多的信息和文档可能过于冗余

  • 对于少数人来说,广泛框架的特定查找可能很困难(可能影响快速原型设计)

本书所有代码的选择也是 Spring Boot,从第二章“设计策略、指南和最佳实践”和第三章“必要的 RESTful API 模式”的示例中,我们看到了使用 Spring Boot 启动 RESTful API 是多么容易。Spring Boot 内置了许多功能,它是软件行业成熟的框架之一。

开始了解 Light 4j

Light 是一个使用 Java SE 开发的云原生微服务平台,其设计目标是高吞吐量、低延迟和占用小空间。Light 4j 是一个通用 Web/API 框架,内置了不同的框架,如 OAUTH2、门户、日志、消息和度量。

Light 4j 的核心功能

light-4j 平台旨在容器化微服务,并支持从 OpenAPI 规范为 RESTful API 和 GraphQL IDL 为 GraphQL 服务的设计驱动方法,并具有代码生成和运行时模型(用于验证和安全)。

作为平台或框架,它很好地解决了几个技术横切关注点,如审计、负载均衡、身份验证和健康检查,这样服务或 API 开发者可以专注于业务逻辑,不必过多担心这些技术问题,也称为非功能性需求。Light 4j 提供了各种处理逻辑,并将这些非功能性需求从业务上下文中分离出来,以帮助 API 开发者专注于开发业务逻辑。

了解 Light Rest 4j

Light Rest 4j 是一个建立在 Light 4j 之上的框架,旨在加快 RESTful API 的开发和部署。它围绕 Swagger 2.0 和 Open API 3.0 规范设计了多个中间件处理程序。Light-rest-4j 附带 Open API 元数据、Open API 安全、Open API 验证器、Swagger 元数据、Swagger 安全和 Swagger 验证器。

light-code-gen

我们可以使用 OpenAPI 3.0 规范就绪的 light-rest-4j 提供的框架构建 RESTful API 或服务,并使用名为light-code-gen的工具命令行工具来构建项目。它为任何服务请求启用 JWT 范围验证和模式验证。

light-code-gen 工具帮助我们使用规范文件和配置 JSON 文件来构建项目。命令行工具可以像 Java 命令行工具、Docker 命令行工具,甚至可以成为 DevOps 管道的一部分的脚本。light-code-gen 还与我们的最爱 Maven 构建工具一起工作。

选择 Light 4j 而非其他选项

让我们以以下关于 Light 4j 的事实来结束本节,因为它可能有助于您决定是否选择 Light 4j 进行 RESTful API 开发:

  • 基于 Java-SE 的框架

  • 可伸缩的设计

  • 低延迟

  • 小内存占用

  • 几个处理程序作为插件

  • 开箱即用的 OAuth2 集成(以安全为首要设计)

  • 与其他框架易于集成

  • 内置的依赖注入框架

  • 基准测试表明这是最快的 RESTful 框架

  • 由于文档不佳,可能涉及陡峭的学习曲线

  • 市场或行业新加入,因此关于生产系统的反馈还不多

基于 light4j 框架构建的 Light-rest-4j 因其各种特性——轻量级、非常低延迟、设计用于可伸缩性、不是基于 J2EE 而是基于 J2SE,以及其以安全为首要的设计——而非常有前途并越来越受欢迎。

Spark 框架

Spark 是一个由 Per Wendel 创立的微框架,用于以最小的努力用 Java 创建 Web 应用程序,它是一个免费的开源 Java Web 框架,在 Apache 2 许可证下发布。

Spark 框架是一个使用 Java 8 Lambda 表达式(基于 lambda 哲学)构建的快速开发 Web 框架,因此可以帮助用更简洁的方式构建 Web 应用程序,实际上你可以用不到 10 行代码构建一个带有 JSON 响应的 REST API,并在开发 Web API 时提供类似 Node.js 的体验。酷吧,不是吗?

Spark 框架的核心功能

让我们一瞥 Spark 的一些核心特性:

  • 设计用于更快、更轻松地创建 API

  • 它是一个轻量级库

  • 提供简单的接口,我们可以通过这些接口定义路由并将它们分派到我们请求的路径上的函数

  • 遵循 Java 8 lambda 哲学(用更少的行创建 Web API)

使用更少的行创建 API

通常,我们会写以下几行代码来使用 Spark 启动一个 hello world API:

1 import static spark.Spark.*;
2
3 public class MyHelloWorld {
4 public static void main(String[] args) {
5 get("/sayhello", (request, response) -> "Hello Reader");
6 }
7 }

就这些!正如前面代码片段中所示,第一行spark.Spark.*完成了魔法般的操作。通过使用 CURL(curl http://localhost:4567/sayhello),我们可以将以下请求和响应作为前述代码的输出可视化:

Request: 

GET http://localhost:4567/sayhello 

Response: 

Hello Reader 

如前所述代码片段所示,curl 击中应用程序,因此Spark.*的 lambda 函数被触发,客户端(在这种情况下是 curl)获得静态 lambda 函数的输出。这不仅仅是一个简单的 hello world API;我们可以使用 Spark 编写复杂的 RESTful API,因为它支持各种函数,如查询映射、cookies 和会话、过滤器、重定向、异常和错误处理以及视图和模板。

Spark 的优点

现在是时候回顾 Spark(以及我们还将回顾另一个名为Dropwizard的激动人心的框架)的优点了:

  • 基于 Java-8-EE 并基于 lambda 哲学(更简洁)

  • 促进快速开发

  • 使应用开发者能够创建可扩展的 REST API

  • 快速且轻量级

  • 最适合快速原型设计目的

  • 由于 Spark 是 Java EE 的 servlet API 的薄包装,因此在速度因素上得分很高

  • 简化和有效的路由

  • 通过提供用于将 API 端点路由到处理器的简单领域特定语言DSL),提高了生产力

  • 支持 Maven 和 Gradle

Spark 的缺点

任何流行的框架都可能存在缺点,Spark 也不例外:

  • 不像其他框架那样受欢迎(社区较小)

  • 可能不适合大型项目(SQL 和 NoSQL 即插即用特性)

Dropwizard

在本节中,我们将回顾另一个在 Apache 许可下授权的基于 Java 的流行框架,名为 Dropwizard。这个框架是以 K.C. Green 网络漫画系列中的一个角色命名的(gunshowcomic.com/316)。

概述

Dropwizard是一个稳定、成熟的框架,由几个 Java 库组成,但轻量级,旨在通过提供快速开发和部署到生产服务器的功能来帮助 API 开发者。Dropwizard 的主要设计目标是提供可靠、可重用和高效实现所有 Web 应用程序所需的一切,并提供开箱即用的功能,使应用程序能够在生产服务器上部署。框架的可重用库使核心应用程序精简且专注,从而减少了上市时间和维护负担:

图片

如前图所示,Dropwizard支持几个强大的库,我们将在接下来的章节中看到每个库的一些细节。

Dropwizard 的核心功能

Dropwizard 定制了几个高性能的 Java 库实现,例如 Jetty、Jersy、Jackson 和 Metrics。让我们快速浏览一下这些库中的几个。

Jetty 用于 HTTP

Dropwizard 使用 Jetty HTTP 库,并通过其main方法启动一个 HTTP Web 服务器,从而简化了将 Web 应用程序作为简单的 Unix 进程运行,并利用现有的 Unix 进程管理工具。通过将main方法作为启动方式,这个过程导致以下结果:

  • 无需管理传统的繁重 Java 生产流程

  • 解决了 PermGen 问题

  • 无需自定义应用程序服务器的配置

  • 无需单独的部署工具

  • 无类加载器问题

Jersey 用于 REST

如我们所知,JAX-RS 参考实现 Jersey 是开源的;它附带其本机 API 工具包,用于简化 Java 中 RESTful Web 服务和其客户端的开发。Jersey 还公开了大量的扩展 SPI(软件平台基础设施作为服务模型)。Dropwizard 将 Jersey 捆绑为其 RESTful Web 应用程序框架,并帮助开发者编写干净的代码,提供可测试的类,优雅地将 HTTP 请求映射到简单的 Java 对象,流式输出,矩阵 URI 参数,条件GET请求等。

Jackson

对于应用程序开发者来说,一个关键需求是拥有从 JSON 到对象映射器的功能,并允许领域模型直接导出到那些 Java 对象。Dropwizard 通过拥有 Jackson 作为其核心功能之一,以及许多其他功能,满足了这些应用程序开发者的需求。

Metrics

这个 Java 库作为一个强大的工具包,提供了测量在生产中部署的组件行为的方法。结合其他突出的库,如 Ehcache 和 Graphite,Metrics 作为一个提供全栈可见性的库,为我们的 RESTful API 和 Web 应用程序提供了全面的支持。

Liquibase

Dropwizard 包括用于管理数据库脚本修订的开源解决方案。Liquibase 支持各种类型的数据库,以及定义数据库结构的各种文件格式。Liquibase 的亮点是其能够从特定点回滚更改(向前和向后)。

其他值得注意的功能

在我们深入了解其优缺点之前,让我们先看看 Dropwizard 中其他值得注意的库:

  • 日志记录:Logback 和 slf4j 用于高性能和灵活的日志记录

  • Hibernate validator:提供用户输入验证的简单方法,以及生成 i18n 友好错误消息的能力

  • Http 交互:捆绑 Apache HttpClient 和 Jersey 客户端库,有助于与其他 Web 服务进行低级和高级的 HTTP 交互

  • JDBI:提供了一种简单、舒适且直接的方式,通过 Java 后端建立关系型数据库连接

  • 模板:Dropwizard 支持 Freemarker 和 Mustache,这是面向消费者或用户界面的应用程序最简单的模板系统

Dropwizard 的好处

由于我们正在介绍每个框架的优缺点,让我们也简要提及 Dropwizard,以便你可以决定是否使用 Dropwizard。以下是其优点:

  • 一个 Java 框架

  • 内置对配置、应用程序度量、日志记录、操作工具、模板等许多功能的支持

  • 你可以进行快速原型设计

  • 运维友好

  • 非常模块化

  • 开发高性能的 RESTful 网络服务

  • 支持许多开源和独立库

  • 使用 Metrics 实现最佳监控

  • 支持集成和使用多个第三方框架

Dropwizard 的缺点

一些你可能认为 Dropwizard 的缺点因素如下:

  • 使用第三方框架和库维护应用程序可能会带来调试噩梦

  • 虽然有几个强大的库,但可能存在需要特定库但 Dropwizard 不支持(仅使用 Dropwizard 提供的内容)的情况

  • 可能会有一个陡峭的学习曲线

理解 Go 框架用于 RESTful API

Go 是微服务和 RESTful API 中较友好的编程语言之一。Go 是一种通用、过程式编程语言,具有高级功能和简洁的语法。它影响了使用包组装进行依赖关系的高效管理,并支持类似于动态语言的动态模式。

概述

Go(Go 语言程序员)可以采用许多强大的框架来为他们的应用程序开发重用现成的、可扩展的生产级外部包。本节旨在介绍两个基于 Go 语言的框架——Gin-gonic 和 Revel 的某些细节和功能,你可以使用它们来快速启动你的 RESTful API 开发。

Gin-gonic

Gin 是一个用 Go 语言编写的 HTTP 网络框架。Gin 的设计灵感来源于 Martini,另一个 Go 框架,然而,Gin 的性能优于 Martini。Gin 框架声称:“如果你需要惊人的性能,那就来点杜松子酒吧。” Gin-gonic 帮助开发者编写更少的样板代码并构建请求处理管道。

Martini (github.com/go-martini/martini) 是使用 Go 语言开发的框架。其模块化和非侵入式的设计使得框架易于使用。然而,请注意,它已经不再维护或支持。

核心功能

Gin 是一个非常精简的框架,并支持您设计和开发 RESTful 网络服务所需的基本功能和功能。精简版本也包含基本库,您可以创建自己的可重用和可扩展的代码片段。

HttpRouter

HttpRouter 是一个轻量级且高性能的 Go 语言 HTTP 请求路由器;它也被称作 多路复用器(或简称为 mux)。与 Go 语言 net/HTTP 包的默认多路复用器不同,这个定制化的多路复用器有助于绑定路由模式中的路由变量并与请求方法进行匹配。这个路由器的最大优点是其小巧的内存占用,优化了高性能。由于这个自定义路由器采用压缩基数树结构进行高效的长路径匹配,因此使用自定义路由器可以实现大量的路由。

Http2 服务器推送

Gin 支持开箱即用的 Http2 服务器推送功能。服务器推送有助于服务器充分利用其网络资源,从而提高页面加载时间。HTTP/2 引入了一个名为 服务器推送 的概念,允许服务器在资源被明确请求之前将额外的所需资源推送到浏览器。

多模板

Gin 默认只允许使用一个 HTML 模板。然而,有一个定制的 HTML 渲染支持多个模板,即多个 *template. 模板。

上传文件

通过使用 multipart.write,我们可以将文件写入缓存并通过 POST 方法发送到服务器。

其他值得注意的功能

尽管 Gin-Gonic 有许多库并支持多个功能,但以下是一些其最佳特性:

  • 分组路由

  • 写入日志文件

  • 自定义验证器

  • 自定义中间件

  • 使用 jsoniter 构建

Gin-Gonic 的好处

让我们看看 Gin-Gonic 的几个优点:

  • 精简且轻量

  • 零分配路由器

  • 完整的单元测试集

  • 兼容性,新版本发布不会破坏旧代码

  • 高性能和高可扩展性

Gin-Gonic 的缺点

让我们注意这个框架的一些缺点:

  • 可能不适合大型、基于企业的实施

  • 服务器处理能力低,迫使客户端处理工作负载

  • 可能存在陡峭的学习曲线

Revel

在我们的框架列表中,最有前途的一个是 Revel。它功能齐全,旨在为我们提供开箱即用的异步、无状态和模块化能力,以支持我们的 Web API。在接下来的部分,让我们了解一下 Revel 及其功能。

核心功能

Revel 是一个自包含的、几乎全栈的 Web 框架,具有可定制的中间件和外部可配置的框架,Gophers 可以快速开发并部署他们的 RESTful API。在接下来的部分,让我们了解 Revel 的一些基本功能。

路由器

在文件中可以配置 URL 和路由定义,如下所示:

[METHOD] [URL Pattern] [Controller.Method]
GET / MyGoSite.Hello

我们可以用 Revel 以不同的方式路由 URL。以下是一些带有示例的几种路由方法:

  • 固定路径路由:HTTP 方法和路径的固定路径或精确匹配路由,以调用特定的方法和控制器

  • GET/About App.About

  • 使用精确匹配/About 作为路径,并在App控制器上使用App.About作为方法

  • URL:参数路由 - 提取带有前缀的路径段

  • GET /user/:id User.Details

Revel 支持的一些其他路由方法(本节未涵盖)包括自动路由、反向路由、静态服务和清除。

服务器引擎

Revel 服务器引擎的最好部分是,应用程序开发者可以自由地实现他们最喜欢的 HTTP 引擎。Revel 默认使用 Go HTTP 引擎,但它允许我们配置任何其他服务器引擎,例如 fastHttp、New Relic HTTP 或我们自定义开发的 HTTP 引擎。这有多酷?

控制器

在 Revel 框架中,控制器是负责执行 API 逻辑的逻辑容器。控制器将传入的 HTTP 请求信息,如查询参数、路径参数、JSON 正文和表单数据,传递给处理器。

处理器

HTTP 请求路由器负责将传入的请求与预定义的 URL 路径列表进行比较,并调用相应的处理器。处理器负责编写响应头和正文。任何满足http.Handler接口的 Go 对象都可以作为处理器。有趣的是,Go 的 HTTP 包附带了一些可以生成处理器的函数,例如FileServerRedirectHandlerNotFoundHandler

拦截器

在某些情况下,例如请求日志记录、错误处理或身份验证处理,我们需要在框架在特定事件之前或之后调用一个动作,这类调用将通过 Revel 的一个名为拦截器的函数来完成。换句话说,拦截器是由框架调用的函数。Revel 支持三种形式的拦截器——函数拦截器、方法拦截器和控制器拦截器。

过滤器

Revel 拥有名为过滤器的独立功能。过滤器有助于实现水平关注点,例如请求记录、cookie 策略和授权。过滤器是中间件,Revel 的大部分内置功能和请求处理功能都是过滤器。它们是允许它们嵌套的接口。

缓存

Revel 附带一个库,它简化了服务器端、低延迟和临时存储作为缓存的使用。如果你需要利用并最小化对数据库的访问,缓存是一个不错的选择。另一个例子是实现基于 cookie 的会话不优先或不足时用户会话的缓存。

其他值得注意的功能

这里是 Revel 的一些其他值得注意的功能:

  • Websockets:Revel 支持通过 WS 方法或通过将服务器WebSocket参数作为操作使用单个 TCP 连接进行全双工通信。

  • 数据库:通过应用配置(application configuration)数据库部分配置支持数据库。请注意,数据库不是预先配置的,开发者需要使用模块,如 GORM。

  • 测试:Revel 附带预构建的模块,使得编写和运行功能测试用例变得容易。

Revel 的好处

现在我们准备查看 Revel 框架的优点:

  • 热代码重载

  • 综合库

  • 高性能

  • 模块化,它围绕可组合的中间件构建,称为过滤器,实现了大部分请求处理功能

  • 内置测试模块简化了功能测试用例的执行

  • 文档完善

Revel 的缺点

可能使 Revel 不那么吸引人的因素如下:

  • 在大多数情况下,综合库是 Revel 的优势;但在某些情况下,它会使代码库庞大并产生较大的占用空间(与 Go 的精简原则相矛盾)

  • 可能存在陡峭的学习曲线

  • 没有管理包版本的社区标准,开发者需要负责管理和发布必要的最新依赖项(没有向后兼容性)

Python RESTful API 框架

Python 是评分最高的编程语言之一。它还以其更简单的语法、高级、面向对象、健壮和通用编程而闻名。Python 是任何初学者的首选。

Python 概述

自 1991 年发布以来,Python 已经发展并支持了多个用于 Web 应用程序开发、科学和数学计算以及图形用户界面的框架,直到最新的 REST API 框架。在本节中,我们将探讨两个综合框架 Django 和 Flask,以便你可以为开发你的 RESTful API 选择最佳框架。

Django

Django 是一个开源的 Web 框架,也以 BSD 许可证提供,旨在帮助开发者快速创建他们的 Web 应用程序,因为它会处理额外的 Web 开发需求。它包括几个包(也称为 应用程序),用于处理典型的 Web 开发任务,例如身份验证、内容管理、脚手架、模板、缓存和聚合。接下来,我们将回顾使用 Python 构建的 Django REST 框架DRF),并在下一节中使用 Django 核心框架进行 REST API 的开发和部署。

Django Rest Framework

DRF 是一个开源、成熟度高的 Python 和 Django 库,旨在帮助 APP 开发者构建复杂的 Web API。DRF 的模块化、灵活和可定制架构使得开发简单、现成的 API 端点和复杂的 REST 构造成为可能。DRF 的目标是分割模型,泛化线表示,如 JSON 或 XML,并使用描述视图和 API 端点之间映射的序列化器来定制一组基于类的视图,以满足特定 API 端点的需求。

核心特性

在接下来的段落中,我们将简要介绍 Django 的核心功能,然后继续介绍其显著特性。

Web 可浏览 API

此功能增强了使用 DRF 开发的 REST API。它具有丰富的界面,并且 Web 可浏览 API 也支持多种媒体类型。可浏览的 API 意味着我们所构建的 API 将是自我描述的,并且我们创建的作为 REST 服务一部分的 API 端点将返回 JSON 或 HTML 表示。关于 Web 可浏览 API 的有趣事实是,我们可以通过浏览器完全与之交互,并且任何使用程序化客户端与之交互的端点也将能够以浏览器友好的视图对 Web 可浏览 API 进行响应。

身份验证

DRF 的开箱即用功能之一是身份验证;它支持广泛的身份验证方案,从基本身份验证、令牌身份验证、会话身份验证、远程用户身份验证到 OAuth 身份验证。它还支持自定义身份验证方案,如果我们希望实现一个的话。DRF 在视图开始时运行身份验证方案,即在允许任何其他代码执行之前。DRF 通过权限和节流策略确定传入请求的权限,然后根据匹配的凭据决定是否允许或拒绝传入请求。

序列化和反序列化

序列化是将复杂数据(如查询集和模型实例)转换为原生 Python 数据类型的过程。转换有助于原生数据类型(如 JSON 或 XML)的渲染。DRF 通过序列化类支持序列化。DRF 的序列化器类似于 Django 的 FormModelForm 类。它提供了一个序列化器类,有助于控制响应的输出。DRF 的 ModelSerializer 类提供了一个简单的机制,我们可以用它来创建处理模型实例和查询集的序列化器。序列化器还执行反序列化,即序列化器允许将解析后的数据转换回复杂类型。请注意,反序列化仅在验证传入数据之后发生。

其他值得注意的特性

下面是 DRF 的其他一些值得注意的特性:

  • 路由器:DRF 支持自动 URL 路由到 Django,并提供了一种一致且简单的方法将视图逻辑连接到一组 URL

  • 基于类的视图:一种使常见功能可重用的主导模式

  • 超链接 API:DRF 支持各种样式(使用主键、实体之间的超链接等)来表示实体之间的关系

  • 通用视图:允许我们构建映射到数据库模型的 API 视图

DRF 还支持许多其他功能,如缓存、节流和测试,我们不会在此处介绍。

DRF 的好处

下面是 DRF 的一些好处:

  • 可在网页上浏览的 API

  • 身份验证策略

  • 强大的序列化功能

  • 丰富的文档和优秀的社区支持

  • 简单而强大

  • 源代码测试覆盖率

  • 安全且可扩展

  • 可定制

DRF 的缺点

下面是一些可能会让一些打算使用 DRF 的 Python 应用开发者失望的事实:

  • 单体和组件一起部署

  • 基于 Django ORM

  • 学习曲线陡峭

  • 响应时间慢

Flask

Flask 是基于 Werkzeug (WSGI 工具包) 和 Jinja 2 (模板引擎) 的 Python 开发者微框架。它遵循 BSD 许可协议。Flask 非常容易设置和使用。像其他框架一样,Flask 提供了几个开箱即用的功能,例如内置的开发服务器、调试器、单元测试支持、模板、安全 Cookie 和 RESTful 请求分发。接下来,让我们看看另一个强大的 RESTful API 框架,称为 Flask-RESTful

Flask-RESTful

Flask-RESTful 是 Flask 的一个扩展,它为构建 REST API 提供了额外的支持。您永远不会对开发 API 所花费的时间感到失望。Flask-Restful 是一个轻量级抽象,与现有的 ORM/libraries 一起工作。Flask-RESTful 鼓励最佳实践,设置简单。现在让我们看看 Flask-RESTful 提供的核心功能。

Flask-RESTful 的核心功能

Flask-RESTful 自带一些内置特性;本节涵盖了几个独特的 RESTful 特性,因为我们已经用 Django 介绍了最常见的 RESTful 框架特性,并且它们在支持核心特性方面没有太大差异。

资源路由

Flask-RESTful 的设计目标是提供基于 Flask 的可插拔视图构建的资源。可插拔视图提供了一种简单的方法(定义资源方法)来访问 HTTP 方法。考虑以下示例代码:

   class Todo(Resource): 
    def get(self, user_id): 
        .... 
    def delete(self, user_id): 
        .... 
    def put(self, user_id): 
        args = parser.parse_args() 
        .... 

RESTful 请求解析

请求解析指的是一个接口,该接口模仿了用于命令行参数的 Python 解析器接口,称为argparser。RESTful 请求解析器旨在提供对(flask.request)请求对象中任何变量的统一和直接访问。

输出字段

在大多数情况下,应用开发者更喜欢控制渲染响应数据,Flask-RESTful 提供了一个机制,允许你使用 ORM 模型或自定义类作为对象进行渲染。关于这个框架的另一个有趣的事实是,应用开发者不必担心暴露任何内部数据结构,因为它允许一个格式和过滤响应对象。因此,当我们查看代码时,将很明显哪些数据将用于渲染以及如何格式化。

其他值得注意的特性

如前所述,我们已经介绍了一些独特的特性,以下是 Flask-RESTful 的其他一些值得注意的特性:

  • API:这是 RESTful API 的主要入口点,我们将使用 Flask 应用进行初始化。

  • ReqParse:这使我们能够在单个请求的上下文中添加和解析多个参数。

  • 输入:一个有用的功能,它解析输入字符串,并根据输入返回 true 或 false。如果输入来自 JSON 体,则类型已经是本地布尔值,无需进一步解析。

Flask 框架的优点

让我们看看 Flask 和 Flask-Restful 框架的一些优点:

  • 内置开发服务器和调试器

  • 开箱即用的 RESTful 请求分发

  • 支持安全 cookie

  • 集成单元测试支持

  • 轻量级

  • 非常简单的设置

  • 更快(性能)

  • 易于 NoSQL 集成

  • 丰富的文档

Flask 的缺点

这里是 Flask 和 Flask-RESTful 的一些缺点:

  • 版本管理(由开发者管理)

  • 没有浏览 API 的加分项

  • 可能会有一个陡峭的学习曲线

框架参考表

以下表格提供了一些其他显著的微框架的快速参考,包括它们的特性和支持的编程语言:

语言 框架 简短描述 显著特性
Java Blade 为 Java8 设计的快速优雅的 MVC 框架 轻量级 高性能 基于 MVC 模式 RESTful 风格的路由接口 内置安全
Java/Scala Play Framework 适用于 Java 和 Scala 的高性能响应式 Web 框架 轻量级、无状态、友好的 Web 架构基于 Akka 支持高度可扩展应用程序的预测性和最小资源消耗开发者友好
Java Ninja Web Framework 全栈 Web 框架 快速开发者友好快速原型设计纯 Java、依赖注入、一流的 IDE 集成简单快速测试(模拟测试/集成测试)优秀的构建和 CI 支持干净的代码库——易于扩展
Java RESTEASY 基于 JBoss 的实现,集成了多个框架,以帮助构建 RESTful Web 和 Java 应用程序 快速可靠的大社区企业级支持安全支持
Java RESTLET 基于 Java 的轻量级且全面的框架,适用于服务器和客户端应用程序。 轻量级的大社区原生 REST 支持连接器集
JavaScript Express.js 基于 Node.js 的轻量级且灵活的移动和 Web 应用程序 JavaScript 框架 HTTP 实用方法安全更新模板引擎
PHP Laravel 基于 PHP 和 MVC 架构模式的开源 Web 应用程序构建器 直观的界面 Blade 模板引擎默认的 Eloquent ORM
Elixir Phoenix (Elixir) 由 Elixir 函数语言提供动力,一个可靠且快速的微框架 基于 MVC 的框架高应用性能 Erlang 虚拟机使资源利用更佳
Python Pyramid 基于 Python 的微框架 轻量级函数装饰器事件和订阅者支持易于实现和高效

摘要

我们即将结束另一个激动人心的章节,该章节讨论了来自三大编程语言——Java、Go(Golang)和 Python 的独特、高效、轻量级、开发者友好、快速上市、高度可扩展的框架。你可能已经注意到,本章涵盖了最流行的框架及其核心功能,并在过程中提到了一些值得注意的特性。本章为读者提供了一些关于框架优缺点想法。我们讨论了Spring-BootLight 4jSpark FrameworkDropwizardGin-gonic、RevelDjangoFlask。很明显,有多个优秀的框架可供你使用,以启动你选择的编程语言的 RESTful API 开发。但仅一章,只有几页的信息,还不足以涵盖这些框架的伟大之处以及它们带来的价值。我们希望这一章能让你对流行的框架有一个公平的了解,以便你可以启动不仅原型设计,还可以启动生产级的 RESTful 应用程序。在下一章中,我们将探讨将遗留应用程序迁移到有能力的微服务中的最佳实践。

进一步阅读

第十一章:传统现代化到以微服务为中心的应用程序

传统应用程序通常是单体、庞大且不灵活的,包含数百万行代码。它们既不模块化也不现代。在它们的特定部分进行任何更改都非常困难。然而,它们在全球范围内成功运行的大多数商业巨头中做出了巨大贡献。主机服务器是最强大和性能最高的 IT 基础设施,托管和运行各种复杂的传统应用程序。尽管关键任务应用程序正在主机上运行,但现代计算要求主机计算和 Web 规模计算之间的一种结合。也就是说,我们需要易于管理和维护的应用程序。在基础设施方面,我们需要开放、高度模块化、可编程、优化和组织的 IT 基础设施。

因此,推动传统应用程序采用软件工程中引入的新创新。有许多值得注意的架构风格、流程优化技术和工具来加速转型过程。数字化转型迫使我们加快速度。每个组织的首要任务是拥有设计良好的

应用程序,能够部署到本地和云环境,以及独立部署、更新服务、在数小时或数天内部署缺陷修复和新功能,而不是数月。

在本章中,我们将讨论微服务架构MSA)是如何成为高度敏捷、灵活和有弹性的现代应用程序的前进方向的。

本章目标包括以下内容:

  • 描述传统应用程序现代化的需求

  • 阐述为什么应用程序必须现代化才能迁移并在云环境中运行

  • 描述微服务和容器的组合是传统现代化的首选方案

  • 详细说明传统现代化方法

技术要求

读者应熟悉以下流行的技术和工具:

  • 允许使用 Docker 的容器化平台

  • 微服务架构

  • API 网关和管理套件

  • 微服务设计原则和模式

  • 微服务组成(编排和协奏)

  • 云操作

容器和微服务的预览

随着开源 Docker 容器化平台的日益流行,容器化领域以前所未有的速度加速发展。如今,容器化在 IT 专业人员中得到了广泛应用。任何类型的软件都可以通过自动化工具轻松容器化。因此,在公共和私有存储库中提供了标准格式的容器就绪镜像。为了使传统应用程序现代化,使用容器作为最有效的包装机制正在兴起并不断发展。

使用容器来现代化遗留应用程序带来了一些优势。容器化的主要目的是从遗留应用程序中去除基础设施依赖。也就是说,容器化应用程序可以在任何平台和基础设施上运行,而无需任何调整。使用这种容器化范式,基础设施的复杂性立即被消除。因此,遗留应用程序变得可移植,并且可以轻松与其他应用程序集成。

一旦容器化,任何遗留应用程序都可以与第三方应用程序交互。使与网页、云和移动平台兼容变得更容易、更快。借助各种用于交付多容器应用程序的平台解决方案,遗留应用程序可以融合以使其对其所有者和最终用户更加相关。将容器化的遗留应用程序从本地云迁移到远程云,或从一个公共云迁移到另一个公共云变得简单。通过附加外部能力,可以增强遗留应用程序的安全性、稳定性和可扩展性。

因此,容器化现象带来的显著进步直接和间接地影响了遗留系统的现代化。容器化运动有几个关键优势,特别是对于战略性和艰巨的遗留现代化目标。遗留软件可以被细分为许多不同的领域,而这些模块中的每一个都在不同的容器中部署。现在,为了保证更高的性能和吞吐量,不同的容器可以在不同的基础设施上运行。一个 I/O 密集型应用程序模块可以被配置在物理/裸机BM)服务器上。一些容器化应用程序服务可以部署在公共云中,以利用其可用性、可扩展性和安全性功能。因此,通过使用容器进行现代化的遗留应用程序通常能够获得现代化、敏捷和适应性。

通过以下方法实现遗留应用程序的现代化:

  • 重构应用程序:正在产生一个新合适且现代的架构,因此,相应的架构决策、组件、连接性和能力被设计和实施。

  • 迁移应用程序到新平台:遗留应用程序可以部署在新平台上,以便被归类为现代应用程序。

  • 重构应用程序:应用程序被划分为许多组件,这些组件针对特定的业务功能。已识别的业务功能通过微服务进行重写,然后这些微服务相互融合以产生所需的应用程序。

引入微服务架构

正如本书多次提到的,微服务架构(MSA)被定位为下一代应用架构模式。微服务作为独立的项目进行构建、编译和部署。容器是微服务开发和部署的最优单元。每个微服务都拥有自己的数据存储。它们不会被编译成软件解决方案。相反,它们作为独立的服务实现,通过基于标准和通信协议进行集成和编排。

每个服务都通过简单易用的 RESTful API 提供了一个接口。微服务经过精心版本控制,以避免对任何其他用户微服务或应用程序产生不利影响。例如,对于一个面向消费者B2C)的电子商务应用,可能开发一个微服务来提供产品目录。另一个微服务负责采购,另一个用于支付,等等。因此,微服务由分布式团队独立开发,然后部署。

通过利用微服务,正在构建复杂和高级的应用程序。另一方面,大型应用程序正在被拆分为一组微服务。

微服务通常是小型且独立的进程,它们通过定义良好且语言无关的 API 相互通信。它们是可用的、可重用的、互操作的、动态可组合的,并且是自我定义的。微服务通过精心设计的接口向外界展示其独特且以业务为中心的能力。因此,软件集成以规范化的方式得到简化并加快。在任何一个企业级软件系统中,通常会在源代码中复制跨领域的关注点和水平(业务无关)的功能。这些功能可以通过微服务集中化。一些功能需要使用先进技术频繁更新和升级。因此,不仅应用开发,维护方面也是全球企业担忧的因素。这些专门的功能可以构建为微服务,然后暴露出来,以便在软件应用中轻松找到和绑定。

为什么需要遗留系统现代化?

遗留软件应用是几十年前使用过时的编程语言编写的,通常在大型机服务器上运行。它们体积庞大,因此修改和操作的目标非常难以实现。第三方集成是另一个难题。要整合变化(由业务、技术和用户引起)既耗时又容易出错。它们紧密耦合,通常在一个进程中运行。所有这些特性使它们无法修改,也无法进行任何关键性的进步。它们在意义上并不现代,因为它们通常不具备网页和移动功能。确切地说,它们并不敏捷或适应性差。它们抵制技术升级,这使得它们维护成本非常高。因此,通过利用一系列开创性的技术、编程语言、开发框架和平台,从遗留应用迁移到现代化应用已经引起了广泛关注。随着新的架构模式和风格的出现和演变,软件架构师和开发者热衷于采用它们,以推出能够轻松满足企业和人们不断变化期望的竞争性应用。

DevOps 的概念正在席卷 IT 组织,以消除开发团队和运维团队之间的任何摩擦。绿色 field 项目和云原生应用正在使用这个新概念进行部署和运营。本章中有关 DevOps 的描述,DevOps 被定位为数字时代的必备条件。由于来自各个来源的软件解决方案需要不断的调整和变化,DevOps 的重要性正在获得更多的支持。

对于单体应用,功能在源代码中被重复。企业中的多个单元提供它们的功能,这些功能必须与核心应用集成。当一家公司收购另一家公司时,它们的源代码必须集成。这种集成最终导致相同功能的重复。如果需要对单个功能进行任何修改,整个单体应用都必须重新编译和重新部署。从遗留应用的角度来看,确实存在一些缺陷。这就是为什么现代化开始受到关注。

遗留系统到数字化应用现代化

应用程序现代化关乎赋予当前运行的应用程序以满足不断变化的企业目标和客户期望的能力。有许多数字技术和人工智能AI)算法正在涌现,它们发展迅速。我们有大量的边缘/数字化技术,如传感器、执行器、植入物、可穿戴设备、贴纸、标签、代码、芯片、控制器、智能尘埃、微粒、信标和 LED 元件,用于将物理、机械和电气系统数字化。也就是说,我们的底层物理系统实现了数字化,加入了主流计算。

一系列数字技术(云、移动性、社交、物联网和数据分析)因其能够带来令人眼花缭乱的数字创新、颠覆和转型而受到广泛关注。随着 AI 算法的成熟,数字智能领域在理解数字数据方面越来越受到重视。因此,真正的数字化转型涉及数字和数字智能技术。此外,还有大量过时和陈旧的应用程序和技术。要实现真正的数字化转型,这些旧的应用程序必须使用现代化和迁移工具进行修改。我们提到的这些广受欢迎的数字技术必须被利用,以推动遗留应用程序的进步。

实现现代化

许多现代化和迁移专家已经讨论了如何在不降低现有业务工作负载和 IT 服务的情况下实现现代化。进行现代化,无论是选择重新工程化、重新架构、重新平台化等等,都是需要回答的悬而未决的问题。其他考虑因素包括现代化是否有助于应用程序保持其原始的性能水平和能力,例如可扩展性、可用性、可靠性和安全性。以下图表展示了众多应用程序现代化和迁移技术如何有助于将遗留应用程序提升到满足业务需求、市场需求、客户满意度和更高生产力的新应用程序:

图片

来源:https://www.winwire.com/wp-content/uploads/2018/07/Application-Modernization-Assess-Strategize-Modernize.png

开创性的技术和突破性工具使得向知识丰富、事件驱动、面向服务、云托管、流程感知和以人为本的新应用程序的平稳过渡成为可能。

面向遗留应用程序的现代化方法

为了使遗留软件包与利益相关者、合作伙伴、员工和消费者保持相关性,企业组织正要求他们的 IT 团队提供可行的技术来加速和简化遗留应用程序现代化的各个方面。在应用现代化方面,存在几种战略上可行的应用架构、众多方法和最佳实践。现代化的应用程序正在准备在云环境中运行,这些云环境通常是整合的、集中的、共享的、虚拟化的和容器化的,并且是自动化的。简而言之,云环境高度优化和组织,以便极其优雅和敏捷地适应。应用程序必须准备好在云中无任何问题地运行,并实现所有最初设想的好处。MSA 的出现可以帮助加速遗留现代化。

以微服务为中心的遗留应用程序现代化

微服务是细粒度、自我定义和自主的服务,它们产生并维持下一代企业级应用程序。使用 MSA,每个应用程序都被构建为一个微服务的集合。换句话说,当适当地组合多个微服务时,我们得到的是能够胜任的应用程序,这些应用程序是云托管的、面向服务的、事件驱动的、充满洞察力的、企业级的和以人为本的。软件架构师对 MSA 模式在构建生产就绪软件解决方案中的未来派和艺术性角色感到非常兴奋。利用数百个不同和分布式微服务作为应用程序模块的复杂和复杂应用程序的设计吸引了大量关注。MSA 范式被定位为敏捷应用设计方法。这被视为软件工程(SE)中的积极发展,因为有许多敏捷开发技术和框架。此外,大型企业中的 DevOps 文化的稳固性正在加快应用程序在生产环境中的部署。也就是说,开发和部署团队之间的问题和摩擦正在克服,这种赋权导致在各种环境中(开发、测试、预生产和生产)应用程序部署速度更快。因此,开发和部署通过 IT 领域的众多进步得到了增强。

繁荣的 MSA 风格显著简化了应用程序的设计和架构。如果我们的业务工作负载和 IT 服务由微服务组成,它们将变得极其灵活、可扩展、可定制和可配置。我们已经看到了为什么全球企业都非常认真地采用 MSA 模式,以便在日常活动中保持竞争力。

架构师和专家已经提出了一些可行的机制,以实现从单体到基于微服务的应用程序的平稳过渡。现代化步骤正在通过多个阶段完成。第一阶段如图所示(来源:dzone.com/articles/choosing-a-legacy-software-modernization-strategy)。这讨论了识别遗留应用程序的独特业务功能并将它们分离出来,通过提供底层服务实现,通常是多语言,一个 RESTful API 来实现微服务。在服务组合部分,我们讨论了以下图的第二阶段:

图片

服务提取

在第一阶段结束时,我们得到了一些具有自己数据存储设施的微服务。这些微服务本质上是模块化的、互操作的,并且它们本质上能够通过 API 相互交互。广泛推荐的设计方法是针对不同领域设计和开发微服务。广泛使用的是领域驱动设计DDD)。例如,以电子商务应用程序为例。领域包括购物车、支付、运输、通知、信用验证和分析。每个领域都被视为一个业务功能,因此作为微服务实现。一个微服务内可以有多个方法。可以有微服务内和微服务间的通信和协作。

一个域内的微服务应该频繁交互。有时,一个域内的微服务需要与其他域的微服务连接和对应,以完成不同的业务流程和活动。因此,电子商务和电子商务应用程序必然会有几个域和数百个微服务。随着容器成为在容错方式下托管和运行微服务的最佳选择,可能会有数千个容器来运行以微服务为中心的应用程序。

服务组合

第二阶段(dzone.com/articles/choosing-a-legacy-software-modernization-strategy)是进行服务组合。在其他章节中,我们讨论了各种组合技术和工具;有两种方法——编排和协奏。

以下图说明了在建立连接和完成数据处理时,集中式代理/中心/总线如何派上用场:

图片

服务迁移

第三阶段是关于将微服务迁移到云环境(私有、公共和混合)。全球有多个公共云环境,它们提供许多好处,如成本效益、可扩展性、可用性和安全性。微服务可以适应由业务需求、最终用户、期望和技术进步引起的所有变化。规模和简单性,以及频繁的变化,使微服务成为满足企业 IT 各种功能和非功能需求的完美工具。此外,微服务还使从单体应用平滑过渡到微服务应用成为可能。

并非所有应用都适合现代化和迁移到云环境。应用必须持续接受各种调查,以确保其价值和有效性。应用合理化、优化和现代化对于应用持续与其管理员和用户相关至关重要。随着高度优化和组织良好的 IT 基础设施的出现,应用需要能够在新的环境中工作,同时保证所有性能要求。一些应用必须经过精心重构才能迁移到云端。专家们为传统现代化和迁移提出了一系列最佳实践。我们将在下一节中探讨这种方法;更多详情可以在dzone.com/articles/choosing-a-legacy-software-modernization-strategy找到。

以容器为中心的传统应用现代化

传统应用可以直接容器化,以在云环境中呈现为现代化的应用。另一个突出的选择是将传统应用细分为多个组件,其中每个组件都服务于一种业务功能。现在,每个业务功能都可以转换为微服务。因此,有以微服务为中心和以容器为中心的方法来实现传统应用的现代化和迁移到云环境。

任何应用都可以直接容器化,但这种过渡可能不会长期有效。因此,应用必须分割成更小、更易于管理的部分。然后这些部分必须实现服务化,然后容器化。有最佳实践、启用模式、知识指南、成功案例、案例研究、优化流程、集成和有洞察力的平台以及经过验证的程序来简化这种类型的传统修复。一流的框架和自动化工具正在蓬勃发展,以应对与遗产转型相关的复杂性。以下是某位现代化专家列出的一份清单。

第一步是将单体应用程序分解成一组可区分的组件。这些组件可以轻松优雅地实现服务化和容器化。这些容器化镜像存储在公开可用的镜像仓库中。这些组件必须极其模块化(松散或轻度耦合且高度内聚),以便为现代化和迁移的目标做出贡献。这些是业务功能,是任何三层应用程序中典型的中间层组件。

在创建了构成应用程序业务逻辑的微服务集合之后,第二步是构建数据访问服务。也就是说,开发数据服务并将它们公开,以便任何应用程序都可以使用它们来获取完成业务任务所需的数据。这种设置解耦了业务和数据逻辑,因此不会出现任何类型的依赖性引发的问题。根据三层应用程序的规范,数据逻辑层是最后一层。在第一步中,我们专注于创建应用程序容器。在第二步中,我们讨论了存储数据以相应地增强应用程序的卷容器。

最后一步完全是关于测试。对于企业级应用程序,我们可以有多个微服务和它们的实例。此外,作为服务运行时的容器现在也变得多种多样。微服务和它们的许多实例可以在单独的容器中运行,因此在典型的 IT 环境中将会有许多容器;也就是说,对于应用程序,可以有多个交互式和协作的微服务。每个微服务都可以在多个容器中运行,以支持冗余。通过单个微服务的多个容器可以实现广泛需求的高可用性。由于容器的易变性,架构师建议为单个微服务托管多个容器。为了确保高可用性,可以有多个微服务的实例。这样,如果一个服务或容器崩溃,其在其他容器中部署的服务实例就会前来救援。然而,真正的困难在于测试这种相互交织的环境。确切地说,单体应用程序需要调整以成为分布式和复杂的应用程序。尽管现代应用程序是敏捷的、经济的和适应性强的,但以微服务为中心的应用程序的管理和运营复杂性必然会升级。进一步来说,检测错误并调试它们以使应用程序无错误确实是一项繁琐的工作。现在出现了几款用于测试微服务的自动化测试工具。专家们正在探索测试分布式微服务的各种方法。此外,复合微服务的测试流程正在被阐述。

重构和重写

我们一直在讨论微服务架构的强大出现是如何引发对传统系统现代化改造的需求,以便拥抱现代性。众所周知,关键缓解技术一直是分而治之。这个格言不仅适用于构建新的系统,也适用于分解传统系统。由于传统应用程序极其复杂,通过分段方法将它们分割成多个较小的部分也是通过这种方式实现的。基于功能性的隔离是实现分解的一种方式。一旦传统应用程序被细分为多个应用程序模块,真正的现代化改造就开始了;也就是说,有许多应用程序服务需要从陈旧和庞大的应用程序中提取出来。

如前所述,提取的应用程序组件被制作成微服务,以便轻松地将它们迁移到云环境中。应用程序组件和微服务之间存在很大的差距。在功能上,它们几乎相同。但应用程序组件的结构与微服务大相径庭。因此,设计一个可行的机制来弥合应用程序组件和微服务之间的差距至关重要。重构或重写是推荐的方法,以实现现代传统应用程序。将传统软件重构为一系列微服务,并使它们能够根据需要相互通信,被推广为实现现代化的最可靠方法之一。随着微服务架构风格和容器化平台成熟度和稳定性的提高,将大规模应用程序重构为细粒度和精心设计的微服务越来越受到重视。

现代化技术术语

传统现代化可以通过多种方式实现。可以将应用程序细分为多个较小的服务组件,并且每个服务都使用现代编程语言进行重写,同时保证传统应用程序的功能。甚至可以使用最新的架构风格,如 SOA、EDA 或两者的组合,来改变传统应用程序的原始架构。目前运行在集中式和主机服务器上的传统应用程序可以被现代化,以在完全新的平台和基础设施上运行,例如云环境。因此,现代化策略的开发和规划需要考虑多个原则、目标、技术和工具。在不同的层次和水平上,所需的现代化以无风险和战略性的方式进行:

  • 重构通常指的是重新组织应用程序的源代码,以便带来一些清晰度和变化。假设编写的代码不是模块化的,或者由于多年来由不同的团队编写,代码中存在大量重复代码。与新获得的公司软件的集成也可能带来一些复杂性。还有其他几个原因需要现代化。因此,提取代码以满足一些新的要求(业务、技术和用户)被宣传为代码重构。

  • 重构和重新设计都意味着同一件事。通过使用更新技术(如中间件解决方案、数据库系统、执行平台、可视化工具包和软件基础设施解决方案)重构应用程序,可以现代化遗留应用程序。此外,随着设计应用程序组件的新模式的出现,重新设计正在变得突出。随着这些架构和设计的变化,新应用程序可以具有更高的性能、可扩展性、安全性、可用性、弹性和可持续性。有了强大的面向消息的中间件平台和新的数据库,如 NoSQL 和 NewSQL,现代化是一个持续的过程,以满足不断变化的企业需求,并确保客户的满意度。

  • 重构平台涉及将待部署的遗留应用程序迁移到更新的平台和基础设施上,以便获得平台和基础设施的好处。

  • 重写应用程序源代码使用现代编程语言是为了体验新编程语言的独特优势。例如,Ballerina是一种新的编程语言,有些人认为它是编写微服务的最佳语言。这种语言简化了服务、数据和应用程序的集成。

在进行现代化时,以下是一些最佳实践的列表:

  • 我们必须确保重构技术是有效的。在从正在转型的遗留应用程序中提取一个易于处理的应用组件之后,我们必须开始重构或重写;也就是说,我们可以重构应用程序组件的源代码,将其转换为微服务。否则,我们可以重写代码,创建一个不改变原始应用程序组件功能的微服务。然后,想法是采用与最终实现非常接近的版本。

  • 没有必要重构或重写遗留应用程序的其他组件。使用存根机制来检查重构或重写的组件是否模仿了旧行为;也就是说,必须确保现有应用程序组件(尚未重构或重写)与新制定的服务之间建立无缝和自发的连接,以了解是否存在任何偏差或不足。这样,可以保证在遗留现代化过程中几乎不浪费任何时间,而这正是一项耗时的工作。

  • 一旦微服务准备就绪,就必须将其放入一个或多个软件仓库(公共和/或私有)中,以便软件开发人员和测试人员能够访问和使用。这种集中式存储也有助于源代码版本控制。有持续集成CI)解决方案来集成、构建和测试软件服务。随着企业 DevOps 概念的广泛应用,业务应用程序能够实现持续和一致的集成、交付和部署,以便快速将应用程序和服务推向最终用户,而没有任何风险。

  • 随着容器化运动的空前普及,对软件应用进行容器化的关注度也在不断提升。容器化微服务带来了一系列技术优势。有许多文章详细介绍了如何快速交付和部署单个和集体的微服务。随着容器集群、编排和管理平台解决方案的出现,容器化范式正变得深入、参与和普遍。有众多自动化工具来增强与云编排、实现多云容器应用和基础设施即代码相关的各种活动。容器和微服务的融合开辟了日益增长的创新、颠覆和转型。有集成平台能够实现梦想服务时代。

  • 在重构或重写应用程序组件时,必须根据一些优先因素进行选择。并非所有组件都适合现代化。进行正确优先级排序需要一些重要参数。需要考虑的关键参数包括提取的简便性、路线图和可能的风险。一些组件可能会变得过时。因此,优先级在以经济、无风险和战略合理的方式完成现代化工作中起着重要作用。

  • 数据虚拟化是应用模块与遗留应用的数据源实现真实对应的方式。因此,建议不要专注于数据结构转换,而应专注于创建和使用适当的代理,这些代理可以将来自不同数据库的数据转换为标准格式。这种标准化有助于新创建的微服务(无论是通过重构还是重写)连接和交互数据,而不会出现任何重大问题。

微服务小而灵活。使用微服务修复缺陷、安全漏洞和漏洞相当容易。性能提升、根本原因分析和统一威胁管理形成关键业务、流程感知和云启用的复合应用。微服务在生产环境中的快速部署是另一个积极因素。在以微服务为中心的应用中,可以替换特定的微服务以使用新的微服务,而在单体应用中这种功能不可用。因此,添加功能是一个顺畅的过程。

通过微服务进行遗留应用现代化

行业对 MSA 用于现代化遗留应用非常乐观,有许多现实原因。单体应用和以微服务为中心的应用之间存在几个有效差异,如下面的图表所示:

图片

单体应用遵循高度集中的架构,而微服务则采用分布式架构。由于数据规模呈指数级增长以及通用服务器的使用,IT 界正倾向于分布式计算。此外,服务正由全球软件开发者开发并在地理上分布的服务器上部署。因此,分布式计算不能再被轻视。MSA 本质支持分布式计算特性,因此,它正在蓬勃发展。

如前所述,每个微服务完成一个或多个业务功能。也就是说,不同的业务能力是通过在不同的进程上运行的单独微服务实现的。对于电子商务应用,我们可以有购物车、客户分析、支付网关、仓储、运输、补给、库存管理和电子邮件通知等模块的微服务。Web 规模应用正在采用 MSA 以实现业务友好性。

对于经常变化负载的应用(用户数量变化快,可能会有不同数据大小的消息数量突然增加),需要机制来支持动态的、横向的、实时的可伸缩性。通过容器化的微服务,我们可以实现这种可伸缩性,因为容器轻量级且可以在几秒钟内启动。

微服务消除了与单体应用程序紧密耦合的应用组件相关的问题。微服务可以通过各种中间件产品(同步和异步通信)进行集成。由于微服务的解耦特性,微服务可以独立设计、开发和部署在生产环境中。也就是说,更新和升级微服务不会影响以微服务为中心的应用程序的其他组件。一些新颖的测试和部署方法正在出现和演变,以给微服务环境带来最大的动态性。

微服务的区别

微服务是技术无关的,这意味着通过 MSA 风格解决了技术锁定问题。因此,它被宣传为完全符合多语言架构模式的解决方案。并且可以使用任何编程语言编写微服务。有几种微服务开发框架用于快速实施和安装。MSA 支持许多最新的通信和数据传输协议。服务适配器生态系统正在不断增长,以便与不同的应用程序服务集成。微服务的接口非常正式化,因此依赖性问题不会出现。

复合应用程序可以通过静态和动态服务组合方法快速实现。有用于编写配置和组合文件的脚本语言。有数据表示、交换和持久化机制。与容器化运动的紧密联系为微服务加快软件工程任务提供了合适的时间。另一方面,微服务在将遗留和过时应用程序分割成多个交互式和自主微服务方面表现出色。因此,由于 MSA 领域的进步,企业正在通过遗留现代化来提升业务。

代码示例

在 GitHub 仓库中,读者可以找到一些使用 Java 语言开发微服务的示例。然后,还有一些示例展示了如何组合多个微服务以生成过程感知和多容器复合应用程序。遗留重构主要涉及对遗留函数代码段进行更改,以将其呈现为微服务。这些微服务的组合导致了以微服务为中心的现代应用程序。微服务可以从零开始构建。或者,使用自动化工具,遗留应用程序代码片段可以被转换为微服务。

克服主要障碍

然而,还有一些障碍需要克服。本节将列出它们。尽管 MSA 风格被宣称为遗留现代化的最优化和有组织的途径,但商业和 IT 团队面临着一些棘手的问题:

  • 如前所述,微服务导致分布式系统,因此,开发者必须探索如何从紧密集成的单体应用程序中提取各种业务功能,将选定的部分重构或重写为微服务,并保持新形成的微服务与遗留应用程序的其余部分相连,以验证是否一切按预期工作。这不是一项容易的任务,因此它是现代化旅程中的一个明确的障碍。

  • 以前,任何单体应用程序都在单个进程中运行。参与的应用程序组件使用语言特性相互通信。有远程过程调用(RPCs)和远程方法调用(RMIs);进程内通信是风险低且快速的。然而,微服务的调用是通过网络进行的。RESTful 接口是访问远程微服务的占主导地位的方式。网络延迟和其他网络相关问题可能会损害微服务的弹性。也就是说,服务间的通信可能会引起一些与网络相关的问题。微服务被填充了 API。API 是任何微服务与其他微服务(可能运行在同一台机器、机架、楼层甚至地理上分布的云环境中)交互的第一个接触点。API 版本可能不同,微服务 API 之间交换的消息的数据格式可能造成不匹配问题,网络拥塞可能发生,微服务上的不同负载可能导致微服务减速甚至失败。

  • 测试微服务带来了它自己的问题。单体应用程序可以很容易地测试,因为它们有一个结合的单一代码库。微服务的情况并非如此。在微服务的情况下,每个微服务都必须单独测试。进一步来说,微服务还必须集体测试。有一些新的方法可以简化微服务的测试。

由于 DevOps 工具的广泛应用,微服务需要特别考虑。DevOps 的目标是加快服务部署。也就是说,可以选择并部署特定的服务。因此,为了获得 MSA 的所有好处,企业必须接受 DevOps。

毫无疑问,微服务开辟了新的可能性和机会。尽管有几种针对遗留系统现代化的选择,但将以微服务为中心的遗留应用程序现代化被公认为前进的最佳方式。微服务方法是企业实现数字化颠覆和转型的最佳途径。

现代化和迁移遗留应用程序——云环境的作用

技术领域正在不断演变,并且随着新技术的采用和工具的加强而得到巩固。例如,云计算范式已经完全重新定义了 IT 领域。云计算技术使得能够拥有高度优化和组织的 IT 来托管任何类型的业务应用程序成为可能。以前未知的 IT 服务正在被制定和交付,并且新的商业模式正在出现,以满足市场不同部分和细分市场的需求,而现在的市场极为知识驱动。

在最近的一段时间里,我们经常听到关于容器和微服务的话题。它们在推动软件工程进步方面展现出巨大的潜力。云计算之旅也在迅速发展。这种进步意味着旧应用程序需要使用最新的技术进行翻新,以便它们可以在云环境中托管和管理。也就是说,大型应用程序被分割成一组微服务,然后它们被容器化,并存储在容器镜像目录中。容器编排平台随后提供容器资源,获取容器镜像,并将它们部署。

DevOps 概念的广泛使用加速了 IT 运营。创建了一种 DevOps 管道(端到端的工作流程),以按顺序排列将代码带到生产环境所需采取的操作。当任何新制作或更改的微服务的源代码提交到代码仓库时,通常会向选定的 CI 工具(如 Jenkins)发送信号。CI 工具用于构建、测试和集成软件应用程序。然后,持续交付和部署工具将应用程序带到生产环境中。在本章中,我们广泛讨论了如何将传统应用程序现代化为一系列微服务,然后迁移到云环境中。

对云环境的需求

云计算理念为 IT 领域带来了范式转变。企业有意识地采用这项技术,以便与他们的客户和消费者保持相关性。云环境是软件定义的,因此云基础设施(服务器机器、存储设备和网络组件)是可编程的、可网络访问的、可远程监控和管理,并且是可定制的。所有类型的架构都可以虚拟化和容器化。因此,云是整合的、集中的和可共享的。随着云环境的日益集成,我们能够拥有混合和多云环境。进一步来说,随着设备生态系统的稳步增长,人们纷纷建立和维持雾或边缘云,以便实现实时数据捕获、处理、知识发现、传播和执行。也就是说,我们的日常环境(家庭、酒店、医院等)正被各种异构设备所充斥。这些设备主要资源受限,其中一些资源密集。这些设备是深度连接和嵌入式系统。有解决方案和方法可以动态创建设备云。因此,边缘云正在扩展云的格局。有大量的通信和数据传输协议用于实现设备到设备D2D)的集成。

有物联网网关解决方案和代理,用于在底层设备与托管在遥远云环境中的软件应用之间进行调解;即设备到云。

随着设备到云D2C)集成解决方案的可用性和采用率的提高,设备到云D2C)集成正日益受到重视。简而言之,有新的云环境正在出现和演变,以满足不同的需求。也就是说,有公共云、私有云、混合云和边缘云。企业也在考虑多云环境,以避免供应商锁定问题。

随着使能技术的挖掘和支持,联邦云环境可能很快就会出现以满足某些特殊需求。云是高度优化和组织的 IT 基础设施。资源利用率随着云化而显著提高。选择适当的云位置以确保更高的性能。云本质上满足了各种非功能性要求,如可伸缩性、可用性、安全性和灵活性。面向 Web 和客户的应用已经部署在公共云环境中。通过额外的安全措施,如虚拟专用网络VPNs)、防火墙和负载均衡设备以及入侵检测和预防系统,公共云环境得到了保护并变得安全,以增强用户和企业的信心。工作负载受到各种调查并部署在最合适的物理机器/服务器、虚拟机VMs)和容器中。有针对任务和资源的开创性调度解决方案和算法;也就是说,为所有类型的传入作业调度资源是完全自动化的。然后,还有应用节能方法以确保电力节约和减少散热。云化运动带来了成本效率。

因此,IT 基础设施将变得更加敏捷、适应性强且价格合理。这就是为什么云计算模式正成为 IT 旅程中的重要组成部分。企业正通过 IT 的颠覆和转型实现自动化和增强。通过 IT 领域的众多创新来赋权于人是下一个现实目标。因此,我们看到了由通用服务器、设备和高端服务器机组成的云正在形成。有融合和超融合基础设施作为云环境。在最近过去,预测称未来属于网络化和嵌入式设备,它们可以快速形成临时的和特定目的的云,以满足特定的数据处理需求。因此,应用正在作为服务池进行现代化,这些服务正被带到云环境中(公共、私有、混合以及边缘/雾计算)。由于云作为一站式 IT 解决方案在满足各种商业需求方面的日益流行,传统现代化和迁移正受到越来越多的关注。

传统现代化和迁移案例研究

理解了集成和洞察性应用的战略意义后,企业正在制定现代化和迁移计划。如前所述,大多数企业级、云、Web 和嵌入式应用正在被构建为以容器化和微服务为中心的应用;传统应用应通过各种自动化流程和产品进行修改。

Blu Age Velocity (www.bluage.com/products/blu-age-velocity) 以自动化现代化而闻名。这项服务自动化并加速了传统应用程序的现代化。它可以进行逆向和正向工程。也就是说,它可以翻译传统应用程序为以微服务为中心的应用程序。读者可以在 www.bluage.com/references 找到许多自动化应用程序现代化的案例研究和参考资料。

我们都知道有两种主要的数据处理方法。批量或批量处理主要是将数据分批或累积,以便在预定时间开始处理。然而,随着实时数据处理技术和平台的可用性,实时数据处理正在兴起。此外,随着时间的推移,数据开始失去其价值,因此应该立即收集、清洗和压缩数据,以便提取及时见解。在主机时代,由于固有的 IT 资源限制,批量处理是数据处理的主要方法。传统应用程序主要是单线程的,因此在语言级别上无法实现并行执行。随着多线程语言和应用程序的出现,并行执行获得了极大的重视。随着多核和多处理器计算机的普及,基础设施级别的并行处理正在实现。随着虚拟机和容器的出现,拥有这些服务器资源的多个实例使我们能够快速跟踪应用程序的执行。较新的编程语言本质上支持多线程,因此并发处理在当今非常突出。随着云基础设施的不断细分,并行执行任务的目标正在加速。现在,随着边缘、本地和远程云环境的激增,这些限制正在逐渐消失,实时分析正在蓬勃发展。也就是说,以前进行批量处理的传统应用程序正在通过基于云的平台进行现代化,以实现实时处理。

微服务和无服务器计算的组合加速了传统系统的现代化

如前所述,微服务可以被编排以构建更大、更好的应用程序。对于遗留系统的现代化,业务能力被细分为独立的微服务。相反,这些易于管理、独立部署和自主的服务可以组合成流程感知、业务关键和复合服务。微服务在其自己的环境中运行,并以松散耦合的方式与其他服务交互。因此,微服务是隔离的,所以如果某个微服务出现问题,不会影响其他微服务。正因为这种独立性,微服务可以被高级服务替换和替代,跨区域复制、更新和升级而不会影响其他服务。

无服务器计算是各种公共云服务提供商CSPs),如 AWS、Azure、IBM 和 Google 最近推出的一种现象。服务器基础设施正在由 CSPs 准备和照顾,以确保函数的平稳运行。这就是为什么函数即服务FaaS)这个热门词汇被广泛报道的原因。其理念如下。我们最初从裸机服务器开始。然后虚拟机和容器作为应用程序优化的资源和运行时出现。现在,我们正朝着函数的方向发展。许多开发者开始使用代码级别的组件(函数)创建模块化系统。这些代码级别的组件根据需要附加到当前应用程序上,以便使应用程序与其用户相关。库是一系列函数的集合。然后,赋予权力的应用程序被编译和部署,以便以小额费用订阅和使用。这种安排可以保证高性能。但函数的重用性并没有达到微服务的水平。

主要来说,函数的配置管理挑战导致了许多麻烦。也就是说,不同的应用程序项目可能需要不同版本的函数。如果一个函数库被更新,所有依赖该库的应用程序都必须经历一系列的更新、重新编译和重新部署。另一方面,正如本书反复提到的,微服务是自包含的,重用性的目标很容易实现。

无服务器计算正逐渐成为托管和运行微服务的理想基础。因此,很明显,MSA 和无服务器计算将提供一个可扩展和可伸缩的环境。随着云环境中自动化水平的持续提升,服务开发者和组装者无需烦恼设置基础设施组件以运行、验证和展示他们的服务。通过容器,函数被部署为一个可访问的实体。容器可以快速创建以运行函数和微服务。容器因其实时水平可伸缩性而闻名。当用户数量增加时,新实例会立即被配置以应对额外的负载。

摘要

配备 RESTful API 的微服务现在是热门商品。微服务已成为应用开发和部署的最优单元,不仅适用于构建和运行企业级和生产就绪的应用程序,也适用于现代化当前运行的应用程序。也就是说,遗留应用程序正被拆分为一组微服务。由于它们的独特特性,微服务正成为将应用程序迁移到云环境的最合适的单元;也就是说,微服务在构建和运行云启用应用程序方面做出了巨大贡献。新的应用程序正在云环境中直接开发,称为云原生应用程序。本章讨论了遗留现代化、为什么它变得至关重要以及 MSA 模式如何帮助从过时的应用程序创建现代应用程序。

随着微服务架构作为设计和开发下一代业务关键和物联网应用程序最有益的架构风格的空前采用,RESTful 范式获得了新生。也就是说,由于 RESTful API 的简单性和可持续性,微服务越来越多地包含 RESTful API。本书涵盖了 RESTful 服务和 API 如何有助于未来和灵活的 Web/云、移动和物联网应用程序的实用和理论信息方面。

posted @ 2025-09-22 13:21  绝不原创的飞龙  阅读(8)  评论(0)    收藏  举报