如何设计API - 应用编程接口最佳实践

296 阅读9分钟

API是应用编程接口的缩写,一个API使用请求和响应与两个应用程序进行通信,它被暴露给外部用户。

API是如何工作的?

What-are-APIs-Learn-How-API-Works

API是如何工作的

想象一下,你在一家商店里,想买一杯苏打水。但你不能直接走进去拿,因为你是一个局外人--一个外部用户--所以你需要一个链接(与某人交谈并为你的汽水付款)来获得你想要的东西。

你不能链接到货架--数据库--因为它们不能移动或说话。所以这就是卖家--API--的作用。卖家在你和你想要的物品--数据(苏打水)--之间充当中介。

现在,你有一个与货架上的物品沟通的链接,所以你要求苏打水。然后卖家搜索出你想要的苏打水品牌和口味,并把它给你。你付钱,拿着它,然后离开。

刚才发生了什么?

卖家(作为API)在货架上(数据库)查询了所要求的数据。

正如你可能知道的,数据集有不同的形式。API在数据库中查询了一个表,然后在表中搜索了详细的数据。最后,API把你需要的数据发给你。

比方说,你请求一个芬达汽水。

你的请求:

image-184

你的响应:

image-185

响应总是以JSON(JavaScript对象符号)格式。这就是API的工作方式。

如何设计一个API

在设计API时,你应该考虑一些最佳做法,这可以帮助你优化你的API及其响应时间。

正确地命名API

假设你正在创建一个API,向你发送一个特定用户的数据。将API简单地命名为GetUsers 是不明智的,因为这意味着你想获得数据库中的所有用户,而调用这个API的外部用户将期望得到你想得到的响应。

为API起一个好名字的例子

  • 使用一个清晰、简明的名字。

如果你想查询一个苹果的数据库,你把API命名为**"api/fruits/"就没有意义

虽然苹果是一种水果,但它并不是最终用户想要的东西。终端用户想要的是一种特定的水果,所以将其命名为"api/apples/"。

  • 使用能够解释查询的词语;

使用在API中代表资源内容的名词等词语,例如"api/stationery/pens"。这解释了对文具数据库中所有笔的API查询。

这将代替例如"api/stationery/write"。

  • 避免使用特殊字符:

如果终端用户看到像"api/fruits%20?/apple " 这样的API,这可能会使他们感到困惑。 他们将不理解这个API是做什么的,或者它如何查询,或者它将得到什么信息。

必要时定义参数

尽可能地避免使用额外的参数,除非你需要它们。在创建RestFul API时,一些必要参数的例子是。

  • 请求头和cookies:这个参数使用服务器发送给用户的网络浏览器的一小段数据。
  • URL查询字符串:这些参数元素被插入到你的URL中,以帮助你过滤和组织内容或跟踪你网站上的信息。
  • URL路径:这是一个必要的参数,给最终用户或调用API的人提供一个获得正确信息的途径,例如。"/users/""/users/<user_id>/","package/<package_id>"
  • 主体查询字符串/多部分:这个参数设置问题或API的HTTP方法,如POST-- 用于发送数据,或PUT-- 用于更新API中的数据。

那么,你什么时候需要参数呢?比方说,外部用户在一个API服务上进行多次查询,API将查询其他服务以获得用户想要的数据。

这将减慢API服务的速度,但额外的参数在这种情况下是有帮助的。

定义响应对象

用通俗的话说,响应对象是API被触发或调用时的响应属性。一些响应对象是。

  • 标题:这是响应的显示标题,例如,如果该对象返回一些用户信息,则为User。这是一个必要的响应对象。
  • 主题:这是响应的主题,例如用户和任何其他与用户有关的信息,API是要涉及到的。
  • Sender_id:这是创建的发件人或用户的ID。这是一个可选的响应对象,意味着如果不需要,你可以选择不把它添加到响应对象中。
  • Categories(类别):这是该响应对象的类别。如果API返回一个用户的信息,类别将是用户

许多开发者创建了一个包含API服务的所有内容的响应对象--甚至是不必要的信息--希望在用户要求更多细节时不要改变响应对象(因为这需要更多的网络要求)。

不幸的是,这是一个糟糕的API设计实践。当创建一个响应对象时,明智的做法是只返回外部用户需要的信息,因为建立一个大型的微服务会影响到性能和更多。

定义错误对象

当你在外部用户查询数据库时返回错误信息时,信息应该是清晰和简洁的--而不是像"发现错误 " 或"发生错误"这样的通用错误信息。

这应该是响应的标题,而数据或主题部分应该解释发生了什么样的错误。

这是我在创建API错误信息时的个人看法。不要返回一个不必要的错误信息。比方说,一些用户数据的最大字符长度是5,而一个外部用户查询API的用户数据的字符长度是8。

而不是这样做:

image-224

做这个:

image-225

这就解释了终端用户做错了什么,并且格式化向终端用户表明这个错误是一个客户端错误。

使用正确的HTTPS请求方法

当为API服务定义HTTP方法%20operations%2C%20respectively.)时,你必须使用正确的方法,让用户以正确的方式查询。一些HTTPS方法是:

  • POST:如果终端用户要向API发送数据,请使用此方法。
  • GET:如果终端用户要在API查询数据库后检索数据,请使用此方法。
  • PUT:如果终端用户更新数据库中的现有数据,使用此方法。
  • PATCH:如果终端用户需要纠正或替换数据库中的现有数据,请使用此方法。
  • DELETE:如果终端用户从数据库中删除任何信息或数据,使用此方法。

想象一下,一个外部用户想通过发送一个ID来查询用户表,而你设计的API方法使用的是POST 方法。这将限制用户的查询,因为终端用户并没有添加或创建数据,用户不能以他们应该可以的方式进行查询。

相反,使用以ID为参数的GET方法将是最好的,而且应该这样做:

users-path

这将使用户可以选择使用一个ID进行查询,并获得特定的数据。

我建议在定义一个方法之前了解所有的HTTP请求方法,并在请求时返回正确的ID。

确保路由非常清晰,这样用户就可以快速调用我之前展示的API服务。

不要在API上产生副作用

副作用是指,例如,当一个外部用户向API查询用户的名字时,它返回的是ID和全名。

当创建一个API时,尽量不要在一个函数中定义所有的东西。如果API设置了许多标志或同时做了许多任务,就应该分成多个API。这就是原子性发挥作用的地方。

原子性是 指多个操作被归入一个单一的逻辑实体。在创建API时,原子性很重要。在使用原子性的时候,对一个函数进行糟糕的命名是一个糟糕的想法。

什么时候需要原子性?

想象一下,我们想让一个用户被创建为管理员,在管理员的组表中。尽管如此,我们还没有创建管理员组表,所以我们的逻辑是创建一个用户作为管理员,创建管理员组表,然后将管理员用户添加到管理员组表中。

*但如果失败了呢?*比方说,用户没有被创建为管理员,但管理员表被创建了,或者反过来。这就是原子性发挥作用的地方。

当使用原子性来调用一个动作时,尽量调用正确的动作,而不是一个通用的动作。否则会给API带来巨大的混乱,而且在使用API时也会出现混乱。

实现分页

当创建一个巨大的微服务,响应体或对象变得太大,分页使API更容易返回少量的信息。

分页是一种将数字内容分离到网站或响应对象的不同页面的方法。

想象一下,一个有七十个用户的数据库。API调用getUsers ,而不是一次性发送所有用户的响应,使其变得缓慢。

你可以将响应分解,比如返回前三十个用户,随后的三十个用户,以及后面的十个用户。虽然分页的响应更快。

但这违反了无状态API的特性,也就是外部用户在他们那端处理会话相关信息的存储。

使用碎片化

当一个API进行内部通信时,响应通常很短。但当它是一个大的响应时,它就是一个例外,而当它是一个例外时,就有问题了。

当响应超过它的极限时就会出现这种情况(每个响应10kb或15kb)。这里的解决方案是将响应分解,并将其一点一点地交给另一个服务。

这就像把TCP(传输控制协议)的数字分解成碎片并给出去,这样服务就不会过载。

它将知道更多的细节还没有到来,而且它还会有一个结束包,就像break命令一样,在片段即将结束时说协议要结束。

总结

以下是本文的一些主要启示。

  • 避免奇怪的字符,使用代表API响应内容的词语。
  • 当响应对象很庞大时,分页和片段是必不可少的。
  • 如果你的数据库有大量的负载,你应该缓存你的请求。
  • 如果你有大量的负载,减少你的响应时间,而不是把全部信息传递给用户。只要传入必要的或关键的数据。这就是所谓的服务降级。它涉及到给出必要的东西,并且仍然响应而不使API服务崩溃。
  • 在设计API时,如果你想获得完美的数据一致性,请缓存你的响应。