代码人生

设计可使用的、灵活的、持久的API

代码人生 http://www.she9.com 2018-09-03 12:07 出处:网络 编辑:@技术狂热粉
我记得那最后一次承诺,就像昨天一样,尽管那是几年前的事了。我们花了六个月的时间来构建我们的应用程序,我们已经准备好了。它遵循了所有的最佳实践,代码是完美的(好吧,也许是足够的),并且它正好赶上了大版本的

我记得那最后一次承诺,就像昨天一样,尽管那是几年前的事了。我们花了六个月的时间来构建我们的应用程序,我们已经准备好了。它遵循了所有的最佳实践,代码是完美的(好吧,也许是足够的),并且它正好赶上了大版本的发布。就像硅谷的一段插曲一样,我们等待着数字的到来——那些数字从未出现过。然而,在6个月的工作之后,我们看到客户表达了兴趣,然后就走开了。


我们设计了一个客户不需要的解决方案。恐慌来了,我们花了三到四周的时间快速重构代码,试图让它成为他们可以使用的东西。但这太少、太迟了。最终,该公司花了大约100万美元在一个被丢弃在垃圾桶中的项目上,这都是因为我们忘记了一个简单的规则:构建API很容易。设计一个可用的、灵活的、持久的API是很困难的。


虽然这段经历让我难以忘怀,但也让我惊讶的是,如此多的大小企业都忘记了这个简单的规则。如果你不明白你正在构建的是什么,你为什么要构建它,或者你为谁构建它——它如何才能真正满足他们的需求?它如何满足你的需求?它是如何成功的?


Who, What, Where, Why, and How

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

一旦你回答了who,下一个问题就变得简单了:他们需要访问什么?通常,这个步骤与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下为消息传递用户提供端点就没有意义了。但是,有一个从users对象到消息的链接是有意义的(允许我们在进行时构建超媒体映射)。


这个过程的下一步是理解我们为什么要构建我们自己的API类型。这一步很重要——它迫使我们考虑选择这种格式而不是其他格式的具体原因,并理解我们选择的格式。

设计可使用的、灵活的、持久的API

大多数API都不是真正的REST API,所以如果您选择构建一个RESTful API,您是否了解REST的约束,包括超媒体/HATEOAS?如果您选择构建部分REST或类似REST的API,您知道为什么选择不遵循REST的某些约束吗?


取决于您的API需要做什么,以及您的API将在哪里使用,遗留格式(如SOAP)可能是有意义的。然而,每种格式都需要在可用性、灵活性和开发成本方面进行权衡。


最后,当我们开始计划我们的API时,重要的是理解我们的用户将如何与API交互,以及他们将如何与其他服务一起使用它。在这个过程中,一定要使用RAML或Swagger/ oaie等工具来让您的用户参与,为他们提供模拟api进行交互,并确保您的设计是一致的,并满足他们的需求。

最佳实践

在设计API时,还需要记住,您正在为以后的构建打下基础。消灭API最简单的方法之一就是在不进行大量测试的情况下重新发明或改进产品中的现有标准/思想。


换句话说,作为一名开发人员,不要花太多心思。构建在经过测试和尝试的最佳实践之上,您的消费者将期望在您的API中使用这些最佳实践。

Nouns vs. Verbs

在构建RESTful或rest式API时,将名词用作资源。


您应该使用/users,然后依赖HTTP方法,而不是依赖/getUser或/deleteUser。另一个更有争议的最佳实践是对返回集合的任何资源使用复数命名约定。请记住,即使资源当前只返回单个项,如果有可能返回多个项,那么应该将其创建为一个集合。


例如,您可能有一个资源/地址,它只返回一个地址。但是,如果允许使用账单地址、送货地址或多个地址,会发生什么情况呢?从第一天开始,您就应该调用资源/地址,并将地址以数组的形式返回,以便应用程序最终允许使用多个地址,并且契约保持不变。

Accept and Content-Type Headers

避免代价高昂的重构的最佳实践是使用accept和content-type报头。无论您的API现在是否只接受JSON或XML,总有一天您需要添加额外的格式。


通过从一开始就在上下文切换器中构建,添加任何新格式都非常简单,只需在幕后添加适当的库并允许这种格式类型,就可以轻松地支持多种格式,而无需重构RESTful API。

Use of Http Methods

因为RESTful或rest式API的操作是由HTTP方法决定的,所以理解每个方法的作用以及何时使用非常重要。也许使用CRUD最简单:

  • CREATE — POST

  • READ — GET

  • UPDATE — PUT, PATCH

  • DELETE — DELETE

虽然许多HTTP方法看起来非常清晰,但是RFC标准允许对每种方法进行独特的使用。例如,POST是一种神奇的方法,可以用于大多数操作;然而,这并不意味着它应该这样做。相反,依赖POST在集合中创建新项或创建新记录。另外,虽然POST在集合上有意义,但在项目级别上可能没有意义。


PUT是另一种独特的方法——它可以用于创建不存在的新记录(但仅当提供显式ID、记录不存在、返回201状态时),或用于更新现有记录。需要记住的是,许多开发人员并不熟悉PUT的create方面,许多框架不会测试结果是200还是201。因此,当他们期望404 NotFound时,他们可能会收到成功的响应。在使用PUT创建新记录时要非常小心,即使在那些特定的情况下也是如此,除非您的API中有充分的必要文档。


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


还要记住,您可能不希望对集合进行PUT、PATCH或DELETE,因为这实际上会更新或删除集合中的所有项。

描述性的错误消息



在理解如何使用API的同时,理解为什么在使用API时有些东西不能正常工作也很重要。我无法告诉您我使用了多少个api,其中错误消息只是“出了问题”或“无效参数”。这些消息无助于开发人员使用您的API。


相反,提供清晰的、相关的错误消息。不要只是告诉用户一个ID丢失了——告诉他们为什么他们一开始就需要这个ID。如果他们在给你的团队发邮件时可以使用内部支持代码,那就给他们那个代码。最重要的是,提供文档链接,向他们展示如何修复错误。您不必重新创建车轮或猜测错误消息中应该包含什么。


目前已经存在几种很棒的格式,包括谷歌错误、JSON API和vnd.error。

Use Hypermedia


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


想想现在是如何构建api的:客户端接收到一个响应,基于这个响应,需要确定它可以对用户采取什么行动。它可以通过使用OPTIONSmethod(如果API允许的话)或调用来查看它们是否工作(获得200或400),或者开发人员可以在客户端模拟业务逻辑并希望没有任何变化。


HATEOAS使用链接告诉客户接下来可以采取什么行动。如果业务规则发生变化,链接也会发生变化——这意味着客户端或开发人员端不需要重新工作。这一点至关重要,因为它还允许客户端和服务器的独立发展。


举个例子,有三个用户:一个是管理员,一个是标准用户,一个是垃圾邮件。很可能,每个对象的应用程序状态都不同。超级用户不能被删除,标准用户可以被挂起,被挂起的用户不能再被挂起。响应中返回的链接以不需要客户端知道底层业务规则的方式解释了每个用户的状态。


也许超媒体最常见的例子可以在万维网上找到。如果你访问DZone.com,你不需要输入你正在寻找的文章的URL。你去DZone.com,点击一个链接,点击另一个链接,最终到达你想去的地方。这一切都是因为HTML(超文本标记语言)。


虽然HTML可能是最常见的超媒体形式,但现在有几种流行的API格式可供使用,包括HAL和JSON API。随着这些规范的成熟,我们还看到了更新的格式,如Siren和CPHL——它们用HTTP方法、表单、代码和灵活的文档扩展了HAL格式。

Example of HAL/CPHL in an API:

{
  "firstName": "Mike",
  "lastName": "Stowe",
  "_links": {
    "edit": {
      "href": "/users/1",
      "methods": [
        "put",
        "patch"
      ]
    },
    "message": {
      "href": "/message?to=1",
      "methods": [
        "post"
      ]
    }
  }
}

超媒体也允许契约的灵活性。假设您有一个消息传递资源,假设您的某个用户意外地将其放置在一个无限循环中,导致10,000次发送,然后到达其极限。要解决这个问题,您需要向资源添加一个令牌——但是如果资源是硬编码的,那么您就违反了约定,这意味着您需要对API进行版本化,并且您的所有客户端都需要更新它们的代码。


对于超媒体,由于它们依赖于一个键:value, URL可以更改,或者可以添加令牌,而不会破坏契约或给用户带来任何不便。


换句话说,这是对上述响应的一个简单更改,这样您就可以在不违反您的api约定的情况下添加额外的检查:

"message": {
 "href": "/
message?to=1&token=893hdy83ikaherukaehfiwf7w48ika",
 "methods": ["post"]
}

回顾一下


构建一个API很容易,但设计一个足够灵活的API却很难——更不用说增加可用性了。这些最佳实践,以及RAML或Swagger/OAI等工具,可以帮助您将挑战变为现实。但是,您需要确保自己花时间,让用户参与进来,并全面考虑API的各个方面。就像地基一样,一块石头放在了错误的地方,会导致整个建筑倒塌——而使用API,只需一件小事就能显著降低其使用寿命。


请关注公众号:程序你好
0

精彩评论

暂无评论...
验证码 换一张
取 消