REST API设计优秀实践之参数与查询的使用

译文
开发 前端
在本文中,我们将针对REST API的设计优秀实践,和您探讨有关参数化和查询字符串用法。

[[322923]]

【51CTO.com快译】众所周知,我们设计API的目标往往是要通过我们的服务,为用户提供一定的功能。虽然HTTP和URL资源都允许数据流进行一定程度的基本交互,但是它们在面对其他特定需求时,往往会让您的API显得力不从心。在此,我们以分页为例,即:如果某个数据库中存放着上百万篇的文章,那么我们很可能无法在单次响应中,将每一篇文章都发送给客户。针对此类需求,业界提出了参数化(parametrization)的解决方法。

什么是参数化?

通常而言,参数化是一种请求配置。在编程语言中,我们可以通过某个函数来请求对应的返回值。如果一个函数不带任何参数,那么我们将无法直接去影响它的返回值。

API也是如此,尤其是那些无状态的REST API。毕竟,所有REST的交互都是无状态的。也就是说,每个请求都会包含那些方便连接器理解该请求的所有信息,而且它们与之前的任何请求都无关。

在实际应用中,我们有许多种方法可以向HTTP请求中添加参数,其中包括:查询字符串,POST、PUT报文,各种PATCH请求、以及标头(header)。它们各自都有自己的用例和规则。

那么,添加所有参数数据的最简单方法,就是将所有内容放入报文内。在具体应用中,每个端点都会用到POST方法,而许多API也会把所有的参数放置在报文中。长此以往,传统的API里积累了越来越多的参数,以致于它们不再适合查询字符串等操作。显然,我们应当避免在设计API时此类极端情况的发生。

添加何种参数?

就REST而言,作为API查询语言的GraphQL,为用户提供了满足数据查询的运行时。那么,我们是否应当添加那些在HTTP规范中已经标准化的标头字段类来作为参数(请参见:http://www.rfcreader.com/#rfc2616_line4589)呢?

在我们了解了各种默认标头字段后,下面我们来讨论是否应该为自己的参数创建一个自定义的标头字段,或者将其放入URL的查询字符串中。

何时该使用查询字符串?

如果我们已获悉待添加的参数不属于默认的标头字段,且并不敏感,那么我们就应当通过查看查询字符串,以确认是否合适。在查询字符串的时候,有个<isindex>的HTML元素,可被用于向服务器发送一些关键字。而服务器则会据此做出响应,并列出与关键字相匹配的页面列表。接着,查询字符串会被重新用于Web表单,以通过GET的请求方式,将数据发送到服务器处。

因此,查询字符串的主要用例便是过滤,它会着重过滤搜索和分页这两种特殊的情况。有关此方面的详细讨论,请参见:https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/?utm_source=dzone&utm_medium=paid&utm_campaign=placed%20article&utm_term=rest%20api%20design%20best%20practices%20for%20parameters%20and%20query%20string%20usage。不过,正如针对Web表单的目的所示,它可以被用于不同类型的参数。而RESTful API可以将POST或PUT请求与报文一起使用,以实现将表单数据发送给服务器。

我们来看一个有关嵌套表示(nested representation)的参数示例。默认情况下,我们需要返回文章的普通表示形式(plain representation)。当我们将?withComments查询字符串添加到端点时,?withComments会以普通表示形式返回某篇文章的评论,而且仅需要一个请求即可。至于此类参数是应该被放在自定义的标头中,还是查询字符串里,则主要取决于开发人员的个人经验与偏好。

根据HTTP的规范陈述(请参见:http://www.rfcreader.com/#rfc2616_line1761):标头字段可以被视为函数的参数。不过,将查询字符串添加到URL中会明显比创建客户端标头,要更快、更显著。实际上,这些字段充当了请求修饰符,其语义等同于编程语言在方法调用中的参数。

在实际应用中,您会发现:在所有端点上保持相同的参数更适合于标头。例如:身份验证令牌可以由每个请求所发送。而那些高度动态的参数(尤其是仅对少数几个端点有效的参数)则应该被放在查询字符串中。例如:每个端点的过滤器参数都会有所不同。

数组和映射参数

对于开发人员而言,他们可能会经常碰到的一个问题是:如何处理查询字符串中的数组参数?例如,我们可能碰到需要搜索多个名称的需求场景。而通常的解决方案是:使用方括号。如下列代码所示:

不过,HTTP的规范指出:由IPv6 [RFC3513]或更高版本标识的主机通过将IP方括号(“[”和“]”)来区分。这是URI语法中唯一允许使用方括号字符的地方。

我们在许多HTTP服务器和客户端的实现场景中,都应当牢记上述规范。当然,为了简便起见,我们可以采用“多次使用一个参数名称”的另一种解决方案。如下列代码所示:

该方法虽然有效,但是它会导致开发人员体验上的下降。通常情况下,客户端只会使用类似于地图(map-like)的数据结构。而该结构在被添加到URL中之前,会进行简单的字符串转换。而这恰恰会导致后续的数值被覆盖。可见,在发送请求之前,我们需要进行更加复杂的转换。

与此同时,也有人会采用另一种方法:使用“,”字符来分隔数值,直接在URL中出现未经编码的字符。如下列代码所示:

而对于那些类似地图的数据结构,我们也可以使用无需编码的“.”字符。如下列代码所示:

另外,您还可以对整个查询字符串进行URL编码(请参见:https://en.wikipedia.org/wiki/Percent-encoding),以便直接使用任何想要的字符或格式。不过值得一提的是,这同样也会大幅降低开发人员的使用体验。

何时不该使用查询字符串?

在实际应用中,由于查询字符串往往是URL的一部分。而那些隐藏在客户端和API之间的攻击者(如:中间人,MIM)则可以轻而易举地读取到我们的URL,因此我们不应该简单地将诸如密码之类的敏感数据,直接放入查询字符串之中。

虽然大多数HTTP客户端在URL中都会允许使用五位数(five-figure)长度的字符,但是如果我们未能全面设计和考虑URL的整体长度,那么开发人员在调试此类字符串时,往往也会经历各种繁琐和不便。

当然,由于我们能够将任何内容都定义为资源,因此我们有时候完全可以使用POST端点,来进行大量参数的调用与传递,进而将报文中的所有数据都发送给API。

可见,为了避免向查询字符串中那些具有多个参数的资源发送GET请求,进而导致冗长且不可识别的URL产生,我们可以设计出诸如搜索类型的资源,根据API的需求,来缓存各种计算的结果。同时,我们还可以向/searches端点发布新的请求。而该请求会将我们的搜索相关配置与参数保存在报文之中。通过返回一个搜索ID,以便我们使用它来获取搜索的结果。

总结

作为API设计人员或架构师,我们所追求的优秀实践,实际上就是要找出API的最理想使用方式,以及最简单的实现用例。综上所述,我们建议您注意如下两个方面:

  • 从一开始就能够借助Moesif(人工智能API服务平台)之类的服务,着手分析自身API的使用模式。
  • 嵌套式资源虽然可以提高URL的可读性,但是如果我们嵌套得太多的话,则会导致URL参数的冗长。因此,如果您发现自己创建了一个具有超大的查询字符串端点,那么我建议您最好从其中提取一部分子资源,放置到报文中作为参数发送出去。

原文标题:REST API Design Best Practices for Parameters and Query String Usage,作者:Kay Ploesser

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

责任编辑:庞桂玉 来源: Linux中国
相关推荐

2022-02-10 23:38:23

API架构设计

2024-08-26 15:35:40

2024-01-11 11:25:22

2024-01-15 08:00:00

开发API文档集成

2021-12-15 09:00:00

GraphQL安全漏洞

2023-02-03 17:29:46

2024-03-12 09:55:24

2023-05-04 16:08:43

2024-06-24 00:20:00

API应用程序接口

2021-08-09 11:35:40

设计实践应用

2019-01-21 14:20:26

Java开发代码

2016-12-27 08:49:55

API设计策略

2023-09-21 11:20:46

2023-12-06 07:13:16

RESTAPI客户端

2021-03-11 14:33:28

Kubernetes开源容器

2021-12-15 09:51:42

Web开发数据

2014-04-18 10:58:44

AndroidAPI实践

2023-05-11 12:40:00

Spring控制器HTTP

2023-10-30 18:59:38

REST API开发

2022-05-13 08:17:05

HTTPRESTful架构
点赞
收藏

51CTO技术栈公众号