
人工智能(AI) VS 商业智能(BI) 区别与联系是什么?
无论 API 是公开的还是内部使用的,API 设计正在成为 API 产品策略的核心支柱。良好的 API 设计可以改善任何 API 程序的整体开发人员体验 (DX),并且可以提高性能和长期可维护性。
然而,没有标准或官方的 API 设计指南。 RESTful只是一种架构风格。有许多针对 API 设计的初学者 API 指南可供使用,例如本指南。然而,我们没有找到很多关于更高级的过滤和分页的 API 指南,这激发了我们发布这篇文章。
URL 参数是将基本过滤添加到 REST API 的最简单方法。如果您有一个销售商品的客户端,您可以通过属性名称作为或条件进行过滤 。但是,这仅适用于精确匹配。如果您想要确定价格或日期范围等范围,该怎么办? /items
GET /items?state=active
GET /items?state=active&seller_id=1234
问题是 URL 参数只有一个键和一个值,但过滤器由三个组件组成:
有多种方法可以将三个组件编码为 URL 参数键/值。
对运算符进行编码的一种方法是在键名上使用方括号。例如,将查找价格大于或等于 10,但小于或等于 100 的所有商品。 []
GET /items?price[gte]=10&price[lte]=100
我们可以根据需要拥有任意多个运算符,例如 [lte]、[gte]、[exists]、[regex]、[before] 和 [after]。
LHS 括号在服务器端解析起来有点困难,但为客户端的过滤器值提供了更大的灵活性。无需以不同方式处理特殊字符。
var qs = require('qs');
var assert = require('assert');
assert.deepEqual(qs.parse('price[gte]=10&price[lte]=100'), {
price: {
gte: 10,
lte: 100
}
});
GROUP BY
GROUP BY
与括号方法类似,您可以设计一个 API 将运算符置于 RHS(而不是 LHS)上。例如,将查找价格大于或等于 10,但小于或等于 100 的所有商品。 GET /items?price=gte:10&price=lte:100
GET /items?user_id=gt:100
如果您需要在端点上进行搜索,您可以直接使用搜索参数添加对过滤器和范围的支持。如果您已经在使用 ElasticSearch 或其他基于 Lucene 的技术,您可以直接支持 Lucene 语法或 ElasticSearch 简单查询字符串。
例如,我们可以搜索包含术语 red chair 且价格大于或等于 10 且小于或等于 100 的商品: GET /items?q=title:red chair AND price:[10 TO 100]
此类 API 可以允许模糊匹配、增强某些术语等等。
大多数返回实体列表的端点都需要某种分页。
如果没有分页,简单的搜索可能会返回数百万甚至数十亿的点击量,从而导致额外的网络流量。
分页需要隐式排序。默认情况下,这可能是项目的唯一标识符,但也可以是其他有序字段,例如创建日期。
这是最简单的分页形式。限制/偏移在使用 SQL 数据库的应用程序中变得流行,这些数据库已经将 LIMIT 和 OFFSET 作为 SQL SELECT 语法的一部分。实现限制/偏移分页只需要很少的业务逻辑。
限制/偏移分页看起来像。此查询将返回从第 100 行开始的 20 行。 GET /items?limit=20&offset=100
(假设查询按创建日期降序排序)
GET /items?limit=20
GET /items?limit=20&offset=20
GET /items?limit=20&offset=40
作为 SQL 语句,第三个请求如下所示:
SELECT
*
FROM
Items
ORDER BY Id
LIMIT 20
OFFSET 40;
GET /items?offset=0&limit=15
GET /items?offset=15&limit=15
GET /items?offset=25&limit=15
即使有限制,偏移分页也很容易实现和理解,并且可以用于数据集上限较小的应用程序。
键集分页使用最后一页的过滤器值来获取下一组项目。这些列将被索引。
(假设查询按创建日期降序排序)
GET /items?limit=20
GET /items?limit=20&created:lte:2021-01-20T00:00:00
GET /items?limit=20&created:lte:2021-01-19T00:00:00
SELECT
*
FROM
Items
WHERE
created <= '2021-01-20T00:00:00'
ORDER BY Id
LIMIT 20
键集分页对于具有单个自然高基数键的数据非常有效,例如可以使用时间戳的时间序列或日志数据。
查找分页是键集分页的扩展。通过添加 after_id 或 start_id URL 参数,我们可以消除分页与过滤器和排序的紧密耦合。由于唯一标识符本质上是高基数,因此我们不会遇到与按低基数字段(如状态枚举或类别名称)排序不同的问题。
基于搜索的分页的问题是当需要自定义排序顺序时很难实现。
(假设查询按创建日期升序排序)
GET /items?limit=20
GET /items?limit=20&after_id=20
GET /items?limit=20&after_id=40
寻求分页可以被提炼成一个子句 where
SELECT
*
FROM
Items
WHERE
Id > 20
LIMIT 20
如果按 id 排序,上面的示例可以正常工作,但如果我们想按电子邮件字段排序怎么办?对于每个请求,后端需要首先获取标识符与 after_id 匹配的项目的电子邮件值。然后,使用该值作为过滤器执行第二个查询。 where
让我们考虑一下查询,后端将需要两个查询。第一个查询可能是使用哈希表进行 O(1) 查找,以获得电子邮件数据透视值。将此输入到第二个查询中,以仅检索电子邮件位于 after_email 之后的项目。我们按电子邮件和 ID 两列进行排序,以确保在两封电子邮件相同的情况下有稳定的排序。这对于较低基数字段至关重要。 GET /items?limit=20&after_id=20&sort_by=email
1.
SELECT
email AS AFTER_EMAIL
FROM
Items
WHERE
Id = 20
2.
SELECT
*
FROM
Items
WHERE
Email >= [AFTER_EMAIL]
ORDER BY Email, Id
LIMIT 20
Seek 分页是一个很好的整体分页策略,也是我们在 Moesif Public API 上实现的。它需要在后端进行更多的工作,但确保不会给 API 的客户端/用户增加额外的复杂性,同时即使在更大的搜索范围内也能保持性能。
与过滤一样,排序对于任何返回大量数据的 API 端点来说都是一项重要功能。如果您要返回用户列表,您的 API 用户可能希望按上次修改日期或电子邮件进行排序。
为了启用排序,许多 API 添加了 sort 或 sort_by URL 参数,该参数可以将字段名称作为值。
然而,良好的 API 设计可以灵活地指定升序或降序。与过滤器一样,指定顺序需要将三个组件编码为键/值对。
GET /users?sort_by=asc(email)
和 GET /users?sort_by=desc(email)
GET /users?sort_by=+email
和 GET /users?sort_by=-email
GET /users?sort_by=email.asc
和 GET /users?sort_by=email.desc
GET /users?sort_by=email&order_by=asc
和 GET /users?sort_by=email&order_by=desc
不建议使用排序和顺序不配对的最后一种设计。您最终可能允许按两列或更多列排序:
SELECT
email
FROM
Items
ORDER BY Last_Modified DESC, Email ASC
LIMIT 20
要编码此多列排序,您可以允许多个字段名称,例如
GET /users?sort_by=desc(last_modified),asc(email)
或
GET /users?sort_by=-last_modified,+email
如果排序字段和排序不配对,则需要保留 URL 参数排序;否则,什么排序应该与什么字段名称配对是不明确的。然而,许多服务器端框架在反序列化到映射后可能不会保留顺序。
您还必须确保任何缓存键都考虑 URL 参数排序,但这会给缓存大小带来压力。
良好的 API 设计是开发者体验 (DX) 的关键组成部分。 API 规范可以比许多底层服务器实现更持久,这需要考虑 API 的未来用例。