无论你使用的是 aws-apigateway(v1)模块管理的 REST API,还是 aws-apigatewayv2(v2)对应的 HTTP API,在 AWS API Gateway 中重命名路径参数这件事,表面上看似简单,实际操作时却隐藏着许多陷阱。今天我们就来详细拆解整个过程,并提供两种经过验证的可行方案,帮助你平稳完成修改,避免对用户产生影响。

首先,我们看两段典型的 CDK 配置示例。REST API 这边,通过 resourceForPath 直接挂载方法:
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
const getArticle = new lambda.Function(this, 'GetArticle', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('path/to/getArticle/code'),
});
const restApi = new apigateway.RestApi(this, 'MyRestApi');
restApi.root.resourceForPath('/get-article/{id}')
.addMethod('GET', new apigateway.LambdaIntegration(getArticle));
HTTP API 的写法也类似:
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
import * as integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
const getArticle = new lambda.Function(this, 'GetArticle', {
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('path/to/getArticle/code'),
});
const httpApi = new apigatewayv2.HttpApi(this, 'MyHttpApi');
httpApi.addRoutes({
path: '/get-article/{id}',
methods: ['GET'],
integration: new integrations.LambdaProxyIntegration({
handler: getArticle,
}),
});
现在假设业务需求发生了变化:你不仅要通过文章 ID 检索文章,还要根据它所属的 Newsletter 来区分。于是你希望将端点路径从 /get-article/{id} 改为 /get-article/{newsletterId}/{articleId}。为了简化讨论,我们聚焦于将参数 id 重命名为 articleId 这一目标。
看起来只是改个名称,可当你执行 npx cdk deploy 时,会直接遭遇以下错误:
REST API(v1):
Resource handler returned message: "A sibling ({id}) of this resource already has a variable path part -- only one is allowed (Service: ApiGateway, Status Code: 400, ...HTTP API(v2):
Resource handler returned message: "The provided route key "GET /get-article/{articleId}" has a conflicting variable on the same hierarchical level as "GET /get-article/{id}" (Service:AmazonApiGatewayV2; Status Code: 409; Error Code: ConflictException;
简单说,API Gateway 不允许在同一层级存在两个不同的路径参数名。那么如何绕过这一限制?主要有两种应对思路。
解决方案 1:注释、部署、更新、再部署
优点:操作简单直接,几乎不需要额外逻辑。
缺点:如果你这条路由已经在生产环境中使用,就必须安排维护窗口,停机无法避免。
操作流程如下:
- 先将包含旧路径参数的 API 资源注释掉:
// restApi.root
// .resourceForPath('/get-article/{id}')
// .addMethod('GET', new apigateway.LambdaIntegration(getArticle));
- 执行
npx cdk deploy部署——此时旧路由已被删除。 - 取消注释上面的代码,并将路径参数名从
id改为articleId:
restApi.root.resourceForPath('/get-article/{articleId}')
.addMethod('GET', new apigateway.LambdaIntegration(getArticle));
- 再次执行
npx cdk deploy部署。
这样一来,你并没有真正“重命名”参数,而是先删除再新建。需要留意的是,在两次部署之间的窗口期,这条路由完全不可用。另外,有人可能会尝试在 AWS 控制台手动删除对应资源,虽然个别场景下能成功,但强烈不推荐这样做——很容易导致 CDK 堆栈状态混乱,后续部署会变得非常棘手。
解决方案 2:创建新资源并迁移
优点:用户无感知,实现零停机。
缺点:需要临时维护两个资源,同时要协调客户端代码的更新。
这一方案的核心思路是“先共存,再切换”。
- 首先,保持旧资源不变,同时使用新路径参数创建新资源。例如在旧路径下添加一个版本前缀:
/get-article/v2/{articleId}。
restApi.root.resourceForPath('/get-article/{id}')
.addMethod('GET', new apigateway.LambdaIntegration(getArticle));
// 添加带有新路径参数名称的新资源
restApi.root.resourceForPath('/get-article/v2/{articleId}')
.addMethod('GET', new apigateway.LambdaIntegration(getArticle));
- 随后更新客户端代码,从旧路由切换到新路由。等所有流量都迁移完毕后,再删除旧资源。
- (可选)如果希望最终路径保持简洁,可以进一步去掉
/v2,直接使用/get-article/{articleId},同时确保客户端代码已做出相应调整。
// 移除下面两行
// restApi.root
// .resourceForPath('/get-article/{id}')
// .addMethod('GET', new apigateway.LambdaIntegration(getArticle));
restApi.root
// 去掉 `/v2` 后缀
.resourceForPath('/get-article/{articleId}')
.addMethod('GET', new apigateway.LambdaIntegration(getArticle));
整个过程用户可照常请求旧路由,新路由上线后也不会打断任何服务。唯一需要关注的,是迁移期间要管理好两个资源,以及把握客户端切换的节奏。
结论
重命名 API Gateway 路径参数并不复杂,但需要避开 Gateway 自身的限制。如果你时间紧张,或者生产环境暂时没有活跃用户,方案一完全够用。但如果服务正被大量真实用户调用,方案二才是更稳妥的选择——虽然多出一些工作量,但换来的是零停机无感迁移。

