为什么将分页游标或 id 值编码为字符串是一种常见的做法?

Why is it a common practice to encode pagination cursors or id values as string?

例如 Facebook 图 API:为什么 afterbefore base64 编码数字?

{
  "data": [
     ... Endpoint data is here
  ],
  "paging": {
    "cursors": {
      "after": "MTAxNTExOTQ1MjAwNzI5NDE=",
      "before": "NDMyNzQyODI3OTQw"
    },
    "previous": "https://graph.facebook.com/me/albums?limit=25&before=NDMyNzQyODI3OTQw"
    "next": "https://graph.facebook.com/me/albums?limit=25&after=MTAxNTExOTQ1MjAwNzI5NDE="
  }
}

与纯数字相比,它可能带来什么好处?

如下python日志所示,好处是数据不能更短表示或者数据包含不安全字符:

>>> base64.b64decode("MTAxNTExOTQ1MjAwNzI5NDE=")
'10151194520072941'
>>> len('10151194520072941')
17
>>> len("MTAxNTExOTQ1MjAwNzI5NDE=")
24

如果你的意思是当你说普通数字时使用 base 10(十进制),那么优点是 base 64 更紧凑,使用更少的数字(一个 10 位 base 10 数字(例如 1,000,000,000)可以在 base 64 中仅用 5 位数字表示(例如 F9eEA)),以及(如您所说)隐藏实现细节。

如果你的意思是在说普通数字时使用原始二进制数据,base 64 使用的字符几乎总是可以安全地通过 Internet、URL 等传输,而不会将某些字符解释为控制字符(这是传输原始二进制数据时存在风险)。有关详细信息,请参阅 this other question

无论哪种情况,使用 base64 都有优势。

编辑:

我明白你的意思了,前面列出的优势不适用于这种情况。 Facebook 可能使用 base64 来与其他 API 函数保持一致,并隐藏实现细节。如果他们将来修改它以允许其他字符,以及容忍潜在的格式错误的请求(假设错误发生在 base64 转换之前),这也可能是有利的。

根据 Whosebug What is JavaScript's highest integer value that a Number can go to without losing precision?

中提出的问题,JavaScript 中的最大可能数字是 9007199254740992

如果你比较这些值

9007199254740992    // the JS maximum
10151194520072941   // the Base64 encoded number

如果确实看起来 Facebook 在内部 - 由于我们不知道的原因 - 存储对于 JavaScript 数字精度来说太大而无法处理的值。

因此,在我看来,除了将数字作为字符串处理之外,他们别无选择。

当然,他们可以只使用 "10151194520072941" 作为字符串格式的数字,但一些程序员可能会将其混淆为数字。尽管这种情况很少发生,但他们可能认为对数字进行 Base64 编码可以避免有人将字符串转换为整数的问题。

另外,因为这个是Public API的功能,不是自己的工程师使用的,所以风险更大,因为使用API的人来自不同的地方教育背景。他们可能会不小心使用例如 parseInt 或类似于导致不必要的客户服务请求的数字。

编辑: 使用非常大的数字可能还有另一个目的:检测对 API 的故意滥用。如果他们使用例如随机 UUID 值或连续数值,则任何接近的值都可能是合法的。如果它是 UUID,他们首先必须发出请求以查看它是否是合法条目。拥有庞大的数字基础,可能只有每 1000 个是合法的,或者它们遵循一些其他数学规则,这些规则可以由单个服务器检测到,无需向其他服务器发出请求,对故意制作具有非法值的请求的客户端进行分类变得更多有效,也许可以在它们到达数据库之前过滤掉。

只是为了向消费者提供一致的规格。这样,您可以更改分页方案,但消费者将始终获得 base64 字符串