如何设计一个高可用,稳定且长久的 API

来自: 21CTO(微信号:we21cto),作者:Mike Stowe,编译:乔乔,原文链接

导读:让我们来看如何设计一个可用,灵活,持久的API,以及如何探索最佳实践。



我昨天刚刚做了一次提交,我大概花了六个月的时间构建一个新的应用程序,已准备好上线。它遵循了所有技术的最佳实践,代码几乎是完美的。


在之前我们设计几个API解决方案,但是效果都不太好。在接下来的三到四周中我们快速重构代码,试图让它成为可用的东西,但它还是太晚了。最后,公司花费了大约100万美元却成了垃圾项目,这都是因为我们忘记了一个简单的规则:构建API很容易,而设计一个高度可用、灵活持久的API却是很难的事情。


谁,怎么做,在哪里,为什么做以及该怎样做?


在设计API之前,需要知道为谁构建,是针对内部消费者,客户,第三方开发商还是以上所有人?


如果你能准确回答,下一个问题会变得更简单——他们需要访问什么?


通常,此步骤与HTTP方法(GET,POST,PUT,PATCH)易混淆,但在此问题中,我们应该能够列出需要的操作。


当我们开始考虑用户需要执行的操作时,请将它们放在类似如下表格中。如下所示:


Users

Create user, edit user, reset password, suspend user, message user

Messages

Create draft, send message, read message, delete message


这张表虽然不怎么成熟,但从一开始就提供了明显的优势。如果你正在构建RESTful API,那么已经确定了资源(在本例中为用户和消息)。


该图表还会强制您考虑工作流程。它通过让我们思考每个节点应该存在的位置,来帮助减少重复; 在这种情况下,如果我们有资源或消息,那么在/users下为消息传递用户设置端点是没有意义的,但是从用户对象到消息的链接却是有意义的。


该过程的下一步是了解要构建的API类型。这一步相当重要 - 它迫使我们考虑选择某种格式的具体缘由,慎重选择我们的API 消息格式。如下图:



市面上大多数 API 都不是真正的REST API,如果你选择构建RESTful API,是不是真正了解REST的约束,包括超媒体或者HATEOAS? 如果选择构建部分REST或类似REST的API,你是否知道为什么选择不遵循REST的某些约束?


根据您的API需要执行的操作以及API使用位置,SOAP等传统格式可能有意义。 但是每种格式都需要在可用性,灵活性和开发成本方面进行权衡。


最后我们开始规划API时,要了解用户如何与API进行交互以及如何将其与其它服务结合使用是至关重要的。在这一过程中务必使用RAML或Swagger/OAI等工具来吸引用户,提供模拟API与他们进行交互,并确保API的设计一致并满足用户之需求。


最佳实践


在设计API时,最重要的是要记住,您正在为以后的构建奠定基础。杀死API的最简单方法之一是重新发明轮子或改进生产环境中的现有标准或想法,而不是事先对其进行大量的测试。


换句话说,作为开发者,不要搞一些花哨的架子,只有建立在消费者期望的API,经过测试和尝试过的才是最佳开发实践。


名词与动词


在构建RESTful或类似REST的API时,请您使用名词作为资源。


比如,使用/users然后依赖HTTP方法,而不是起这样如/getUser 或 /deleteUser 的名字。另一个更有争议的技术最佳实践是对返回集合的任何资源时请使用复数命名约定。请各位开发记住,即使资源当前只返回单个项目,如果有可能返回多个项目,则一定应将其创建为集合类型。


例如,目前可能仅返回一个地址的资源/地址。比如使用订单配送地址,这个可能有多个,会发生什么情况?因此从第一天开始,调用资源/地址时应该将数据添加到一个数组中,以便应用程序发展到最终允许多个地址时,协议也保持不变。


接受和内容类型标头


避免代价高昂的重构,最佳做法是使用Accept和Content-type标头。无论你的API目前是否只接受JSON或XML,未来都可能需要添加其它格式内容。


通过从一开始就在上下文中构建切换器,添加任何新格式就像在幕后添加适当的库并允许该格式类型一样简单,让我们轻松支持多种格式而无需再重构RESTful API。


使用HTTP方法


前面提到过,RESTful或类似REST的API的操作由HTTP方法决定,因此了解每个方法的作用以及何时使用它非常重要。


也许最简单的方法是使用CRUD:


  • 创建 - POST

  • 获取 - GET

  • 更新 - PUT,PATCH

  • 删除 - DELETE


虽然许多HTTP方法看起来非常清晰,但RFC标准允许每种方法的独特用法。比如,POST就是一种神奇的方法,可用于大多数操作。但是,这并不意味着它应该承担全部。例如依靠POST在集合中创建新项目或创建新记录。此外,虽然POST对集合有意义,但在单个项目级别可能就没有意义。


PUT是另一个独特的方法 - 如果不存在,它可用于创建新记录(但仅当提供了显式ID,记录不存在并返回201状态),或用于更新现有记录。重要的是,现在许多开发人员不熟悉PUT的创建,并且许多框架也不测试结果是否为200或201。因此,当期望404 Not Found时,他们可能会收到成功的响应。


要非常谨慎使用PUT来创建新记录,即使在这些特定情况下,除非特殊必要情况,并且要在API中有详细日志记录。


PATCH被广泛建议作为PUT的替代品,因为它将使用接收的数据来修补数据记录。

另外还要记住,您可能不希望在集合上允许PUT,PATCH或DELETE,因为这实际上会更新或删除该集合中的所有项目。


描述好错误返回消息


除了要了解如何使用API之外,显示使用API时无效的原因也非常地重要。我不能告诉你使用的API的数量,但错误消息中只是显示“出错了”或“无效参数”等,这些消息无益于其它开发人员使用您提供的API。


因此,我们需要提供清晰且相关性高的错误消息。不要只是告诉用户缺少ID - 而要告诉他们为什么需要ID来开始。如果需要开发者向 API 团队发送邮件时可以使用内部支持代码,在出问题时需要他们提供该编码,另外重要的是要提供文档的相关链接,告诉他们如何修复错误。这样就不必重新创建dashboard或猜测消息中应包含哪些错误内容。


目前已经有几种很棒的格式可参考,例如Google Errors,JSON API和vnd.error。


使用超媒体(HATEOAS)


虽然HATEOAS(超媒体作为应用程序状态的引擎)听起来很极端,但它的目的很简单:从客户端中删除业务逻辑。


想想今天如何构建API:客户端接收响应,并根据该响应,确定它可以对用户采取的操作。它可以通过利用OPTIONS方法(如果API允许)或调用它们来查看它们是否有效(获得200或400),或者开发人员可以模仿客户端上的业务逻辑并希望没有任何变化。


使用HATEOAS 中返回的链接会告诉客户端接下来可以采取什么行动。如果业务规则发生变化,则链接着也会发生变化 - 这意味着客户端或开发人员不需再返工。这是至关重要的,这样也允许客户端和服务器单独演进。


我以三个用户为例:一个是管理员,一个是标准用户,另一个是垃圾邮件。最有可能的是,每个对象的应用程序状态是不同的。无法删除超级用户,可以暂停标准用户,并且不能再次暂停的用户。响应中返回的链接以不需要客户端知道基础业务规则的方式来解释每个用户的状态。


最常见的超媒体示例随意可以在网站上找到。如果您访问 www.21CTO.com,则无需输入您你查找的文章的URL。您转到21CTO.com,单击一个链接,再单击另一个链接,最终到达你想要的位置,这一切都是因为HTML(超文本标记语言)而发生的。


HTML是最常见的超媒体形式,我们现在可以使用几种流行的格式,包括HAL和JSON API。伴随着这些规范的成熟,我们也看到了更新的格式,比如Siren和CPHL--它们使用HTTP方法,表单,按需代码和灵活的文档扩展HAL格式。


API中的HAL / CPHL示例如下:


{

  "firstName": "Mike",

  "lastName": "Stowe",

  "_links": {

    "edit": {

      "href": "/users/1",

      "methods": [

        "put",

        "patch"

      ]

    },

    "message": {

      "href": "/message?to=1",

      "methods": [

        "post"

      ]

    }

  }

}


超媒体还让协议具有很高的灵活性。 假设您有一个消息传递资源,并且假设一个用户意外地将其置于无限循环中,在达到其限制之前导致10,000次循环发送。 要解决这问题,您需要向资源添加一个Token - 但如果资源是硬编码的,那么就违反了协议,这意味着需要对API进行版本控制,那么所有客户端都需要更新或重写代码。


当我们使用超媒体时,由于它们依赖于键值:URL是可以更改的,或者可以添加令牌而不会破坏协议或给用户带来任何的不便。


以下代码是对上述响应内容的简单更改,开发者可以在不违反协议的情况下进行额外检查:


"message": {

 "href": "/

message?to=1&token=893hdy83ikaherukaehfiwf7w48ika",

 "methods": ["post"]

}


小结


构建一个 API 看起来很容易,但设计一个足够灵活的API,让它可以持续使用数年,再加随着平台的增长而不断发展,这变很异常地难 - 更不用说增加可用性了。


本文这些技术最佳实践以及RAML或Swagger/OAI等工具可以帮助API 开发者实现这一挑战。 您需要花一些时间,让使用API 的同学也参与进来,深度思考 API 的各个方面。就像一个地基,有错误的地方就像一个窟窿会导致整个大坝塌方。与此相仿,开发设计 API,只要您一点点粗心就可能大大缩短它的使用寿命,还可能需要重新另造一个轮子。

推荐↓↓↓
Web开发
上一篇:使用无服务器架构的六个月里,我学到的六件事 下一篇:京东商品单品页统一服务系统架构未公开的细节