PAYATHON 2026

API 路由命名规范:如何避免静态路径被误识别为 id?

排障赵师傅

先处理静态路由,再限制 id 的取值

/tasks/archived-count 这个名称可以保留。要避免它匹配到 :id,需要根据框架的路由匹配规则处理:

  • 框架按注册顺序匹配时,把 /tasks/archived-count 放在 /tasks/:id 前面。
  • id 有固定格式时,例如纯数字或 UUID,按实际格式约束参数,并在查询数据前校验。
  • id 允许任意字符串时,约定保留路径名称,或把统计接口与按 id 查询的接口分开。

问题中没有说明使用的框架,因此不能认定静态路由一定优先。下面以 Express 为例。

为什么 archived-count 会匹配到 :id?

没有额外约束时,:id 通常只表示捕获这个位置的一个路径片段。路由器不会因为参数名叫 id,就自动判断它必须是数字、UUID,或数据库中的有效标识符。

因此,下面两个请求都可能匹配 /tasks/:id:

GET /api/v1/tasks/123
GET /api/v1/tasks/archived-count

第二个请求传给动态路由的参数是:

req.params.id === 'archived-count'

在 Express 中,如果动态路由先注册,并直接结束响应,后面的静态路由就没有机会处理请求。

在 Express 中如何配置?

先注册静态路由,再注册动态路由。以下示例假设 task id 只允许 ASCII 数字,处理器由业务代码提供。

const express = require('express');
const {
  handleArchivedCount,
  handleTaskById,
} = require('./task-handlers');

const app = express();

// 静态路由先注册
app.get(
  '/api/v1/tasks/archived-count',
  handleArchivedCount
);

// 动态路由随后注册,并在查询前校验参数
app.get(
  '/api/v1/tasks/:id',
  (req, res, next) => {
    if (!/^[0-9]+$/.test(req.params.id)) {
      return res.status(400).json({
        error: 'Invalid task id',
      });
    }

    next();
  },
  handleTaskById
);

注册顺序和参数校验各有作用:

  • 注册顺序让 archived-count 进入统计处理器。
  • 参数校验让其他格式错误的 id 在查询数据前返回错误。

如果动态路由放在前面,并在校验失败时直接返回 400,请求仍然到不了后面的静态路由。所以,仅增加校验不能替代正确的路由配置。

实际使用 UUID 时,应按项目接受的 UUID 格式校验。原本合法的字符串 id 也应继续支持,不要为了消除冲突临时改成数字。

能否直接在路由中限制参数格式?

这取决于框架及其版本支持的语法。

有些路由器支持参数类型、转换器或正则约束。如果在路由匹配阶段约束参数,不符合格式的片段就不会进入该动态路由。Express 不同版本对路径模式的支持有差异,不能直接套用其他版本的正则写法。

路由约束和处理器校验也需要区分:

  • 路由约束决定请求是否匹配该路由。
  • 处理器中的校验决定匹配之后是否接受该参数。

两者执行的位置不同,失败后的响应也可能不同。

是否需要修改 API 命名?

如果只有一个归档计数接口,可以保留现有设计:

GET /api/v1/tasks/:id
GET /api/v1/tasks/archived-count

如果后续还会增加按状态统计的接口,可以考虑统一为:

GET /api/v1/tasks/count?status=archived

status=archived 表示统计条件。不过,count 仍然可能匹配 :id,依然需要处理路由优先级或参数约束。查询参数不会自动消除路径冲突。

也可以为统计接口增加一层路径:

GET /api/v1/tasks/stats/archived-count
GET /api/v1/tasks/:id

在完整路径匹配、且 :id 只匹配单个片段的前提下,统计路径多了一层,不会匹配 /tasks/:id。如果项目中有前缀匹配中间件或通配路由,还要检查它们是否会提前拦截请求。

id 可以是任意字符串时怎么办?

如果 archived-count 本身也是合法的 task id,同一个 HTTP 方法和路径就有了两种含义:

GET /api/v1/tasks/archived-count

路由器无法判断调用方想获取任务详情,还是归档任务数量。调整注册顺序只能让其中一种含义优先,无法同时支持两者。

这时需要选择一种方案:

  • 将 archived-count 等静态名称设为保留名称,在创建、修改 id 时禁止使用。
  • 使用独立路径,例如:
GET /api/v1/tasks/by-id/:id
GET /api/v1/tasks/archived-count

第二种方案可以让任意字符串 id 与统计路径分开,但修改已有接口时需要考虑客户端兼容性。

上线前需要检查什么?

至少验证以下请求:

请求预期行为
/tasks/archived-count进入归档计数处理器
/tasks/123在数字 id 的前提下进入详情处理器
/tasks/abc按数字 id 的校验规则返回参数错误
格式合法但不存在的 id详情接口返回 404

还要检查路由挂载顺序、参数中间件和通配路由。框架若支持明确的路由优先级,应按其规则配置;不能只凭路径看起来更具体,就认定它会先匹配。

备注:内容仅供参考。