所有文章 > API设计 > REST API 设计参数和查询字符串使用的最佳实践
REST API 设计参数和查询字符串使用的最佳实践

REST API 设计参数和查询字符串使用的最佳实践

当我们设计 API 时,目标是让用户对我们提供的服务拥有一定程度的控制权。虽然 HTTP 动词和资源 URL 允许进行一些基本交互,但通常需要提供额外的功能,否则系统会变得过于繁琐而难以使用。

分页就是一个例子:如果我们的数据库中有数百万篇文章,我们就无法将每篇文章一次性发送给客户。

完成此操作的一种方法是使用参数化

什么是参数化

一般来说,参数化是一种请求配置。

在编程语言中,我们可以请求函数的返回值。如果函数不接受任何参数,我们就不能直接影响这个返回值。

API 也是如此,尤其是像 REST API 这样的无状态 API。Roy Fielding 曾雄辩地说过:

所有 REST 交互都是无状态的。也就是说,每个请求都包含连接器理解该请求所需的所有信息,与之前的任何请求无关。

HTTP 中有很多方法可以向我们的请求添加参数:查询字符串、POST、PUT 和 PATCH 请求的主体以及标头。每种方法都有自己的用例和规则。

添加所有参数数据的最简单方法是将所有内容放在正文中。许多 API 都以这种方式工作。每个端点都使用 POST,所有参数都在正文中。这在旧版 API 中尤其如此,这些 API 在十多年中积累了越来越多的参数,以至于它们不再适合查询字符串。

虽然这种情况很常见,但我认为这是 API 设计中的一个极端情况。如果我们事先提出正确的问题,我们就可以避免这样的结果。

我们要添加什么样的参数?

我们应该问自己的第一个问题是我们想要添加什么样的参数?

也许它是一个已经标准化的 HTTP 规范中的标头字段的参数。

有许多标准化字段。有时我们可以重新发明轮子并将信息添加到另一个地方。我并不是说我们不能以不同的方式做这件事。例如,从 REST 的角度来看,GraphQL 做了我认为疯狂的事情,但它仍然有效。有时使用已经存在的东西更简单。

以标头为例Accept。这允许我们定义响应应采用的格式或媒体类型。我们可以使用它来告诉 API 我们需要JSONXML。我们还可以使用它来获取API 的版本。

我们还可以使用一个Cache-Control标头来阻止 API 向我们发送缓存响应no-cache,而不是使用查询字符串作为缓存破坏器?cb=<RANDOM_STRING>

授权也可以看作是一个参数。根据 API 授权的细节,授权或未授权可能会产生不同的响应。HTTP为此定义了一个标头。Authorization

检查完所有默认标头字段后,下一步是评估是否应该为我们的参数创建一个自定义标头字段,或者将其放入我们的 URL 的查询字符串中。

什么时候应该使用查询字符串?

如果我们知道我们想要添加的参数不属于默认标头字段,并且不敏感,我们应该看看查询字符串是否是适合它们的位置。

从历史上看,查询字符串的用途,顾名思义,就是查询数据。有一个<isindex>HTML 元素可用于将一些关键字发送到服务器,服务器将响应与关键字匹配的页面列表。

后来,查询字符串被重新用于 Web 表单,以通过 GET 请求将数据发送到服务器。

因此,查询字符串的主要用例是过滤,具体来说,过滤有两种特殊情况:搜索和分页。

但正如重新用于 Web 表单所示,它也可以用于不同类型的参数。RESTful API 可以使用带有正文的 POST 或 PUT 请求将表单数据发送到服务器。

一个例子是嵌套表示的参数。默认情况下,我们返回一篇文章的简单表示。当将?withComments查询字符串添加到端点时,我们会以内联方式返回该文章的评论,因此只需要一个请求。

这样的参数是否应该进入自定义标头或查询字符串主要是开发人员经验的问题。

HTTP规范规定标头字段有点像函数参数,因此它们确实被视为我们要使用的参数。但是,在这种情况下,向 URL 添加查询字符串比创建客户标头更快,也更明显。

这些字段充当请求修饰符,其语义相当于编程语言方法调用中的参数。

所有端点上保持一致的参数更适合标头。例如,身份验证令牌会在每次请求时发送。

高度动态的参数(尤其是当它们仅对少数端点有效时)应放在查询字符串中。例如,每个端点的过滤器参数都不同。

额外的:数组和 Map 参数

经常出现的一个问题是如何处理查询字符串中的数组参数?

例如,如果我们有多个名称,我们想要搜索。

一种解决方案是使用方括号。

/authors?name[]=kay&name[]=xing

但是 HTTP 规范规定:

由 Internet 协议文字地址(版本 6[RFC3513] 或更高版本)标识的主机通过将 IP 文字括在方括号(“[” 和 “]”)内来区分。这是 URI 语法中唯一允许使用方括号字符的地方。

许多 HTTP 服务器和客户端的实现并不关心这个事实,但应该牢记这一点。

提供的另一种解决方案是多次使用一个参数名称:

/authors?name=kay&name=xing

这是一个有效的解决方案,但会导致开发人员体验下降。客户端通常只使用类似地图的数据结构,该结构在添加到 URL 之前会经过简单的字符串转换,这可能会导致覆盖以下值。在发送请求之前,需要进行更复杂的转换。

另一种方法是用字符分隔值,,这些字符在 URL 内允许未编码。

/authors?name=kay,xing

对于类似地图的数据结构,我们可以使用.字符,该字符也是允许未编码的。

/articles?age.gt=21&age.lt=40

也可以对整个查询字符串进行 URL 编码,这样它就可以使用我们想要的任何字符或格式。需要注意的是,这也会大大降低开发人员的体验。

什么时候我们不应该使用查询字符串?

查询字符串是我们 URL 的一部分,并且客户端和 API 之间的每个人都可以读取我们的 URL,因此我们不应该将密码等敏感数据放入查询字符串中。

此外,如果我们不认真对待 URL 设计和长度,开发人员的体验就会大打折扣。当然,大多数 HTTP 客户端都允许 URL 中的字符长度为五位数,但调试此类字符串并不是一件令人愉快的事情。

由于任何事物都可以定义为资源,因此有时使用 POST 端点来处理大量参数会更有意义。这让我们可以将主体中的所有数据发送到 API。

我们可以将其设计为资源(例如搜索资源),而不是向查询字符串中包含多个参数的资源发送 GET 请求,这可能会导致 URL 非常长且无法调试。根据我们的 API 需要执行的操作来满足我们的请求,我们甚至可以使用它来缓存我们的计算结果。

我们将向我们的/searches 端点发送一个新请求,该请求在正文中保存我们的搜索配置/参数。将返回一个搜索 ID,我们稍后可以使用它来获取搜索结果。

结论

与所有最佳实践一样,作为 API 设计师和架构师,我们的工作不是遵循一种方法作为“最佳解决方案”,而是找出我们的 API 是如何使用的。

最常见的用例应该是最容易实现的,并且用户很难犯错。

因此,从一开始就分析我们的 API 使用模式始终很重要 – 我们越早获得数据,如果我们搞砸了设计,就越容易实施更改。

如果我们选择一种方法,是因为它更容易掌握或者更容易实现,那么我们必须看看我们能从中得到什么。

虽然嵌套资源可用于使 URL 更具可读性,但如果嵌套过多,它们也会变得太长且难以阅读。参数也是如此。如果我们发现自己创建的端点具有巨大的查询字符串,那么最好从中提取另一个资源并在主体内发送参数。

原文链接:REST API Design Best Practices for Parameter and Query String Usage

#你可能也喜欢这些API文章!