Nest.js部署Cloud Run后遇QueryFailedError:where子句含NaN列
错误信息
QueryFailedError: Unknown column 'NaN' in 'where clause' at Query.onResult (/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:158:37) at Query.execute (/node_modules/mysql2/lib/commands/command.js:36:14) at PoolConnection.handlePacket (/node_modules/mysql2/lib/connection.js:478:34) at PacketParser.onPacket (/node_modules/mysql2/lib/connection.js:97:12) at PacketParser.executeStart (/node_modules/mysql2/lib/packet_parser.js:75:16) at Socket. (/node_modules/mysql2/lib/connection.js:104:25) at Socket.emit (node:events:514:28) at addChunk (node:internal/streams/readable:324:12) at readableAddChunk (node:internal/streams/readable:297:9) at Readable.push (node:internal/streams/readable:234:10)
根本原因
本地环境中req.ip能直接拿到客户端真实IPv4地址,转换正常;但Cloud Run作为托管服务,请求会经过Google的反向代理层,默认情况下Nest.js(基于Express/Fastify)不会信任代理,导致req.ip获取到的是容器内部IP(如127.0.0.1或IPv6格式地址),而你的ipToDecimal函数仅支持标准IPv4转换,无法处理这类异常IP,最终返回NaN,代入查询后触发SQL语法错误。
解决步骤
1. 配置Nest.js信任反向代理
在main.ts中添加代理信任配置,让框架识别Cloud Run的代理请求,从而获取真实客户端IP:
// main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 信任所有代理(Cloud Run环境下安全,因为代理由Google管理) app.enableCors(); // 按需配置跨域规则 app.set('trust proxy', true); // Express底层配置,若使用Fastify需替换为对应代理信任逻辑 await app.listen(process.env.PORT || 3000); } bootstrap();
2. 增强IP转换函数的兼容性
修改ipToDecimal函数,增加IP格式校验与异常处理,避免返回NaN:
export function ipToDecimal(ip: string): number | null { // 校验是否为合法IPv4地址 const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; if (!ipv4Regex.test(ip)) { // 若为IPv6或无效IP,返回null标记异常 return null; } // 原有IPv4转十进制逻辑 const parts = ip.split('.').map(Number); return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]; }
3. 服务层增加错误拦截
在getStoreIdByIP中检查转换结果,避免执行无效查询:
async getStoreIdByIP(ip: string): Promise<StoreIdDto | ProblemDto> { const ipDecimal = ipToDecimal(ip); if (ipDecimal === null || isNaN(ipDecimal)) { return { status: 400, message: '无效的客户端IP' }; } const store = await this.findStoreByIpNum(ipDecimal); // 剩余业务逻辑... }
验证方法
- 部署修改后的代码到Cloud Run
- 在
getStoreByIP接口中添加日志,打印req.ip和转换后的ipDecimal值 - 发起请求后查看Cloud Run日志,确认
req.ip为真实客户端IPv4,且ipDecimal为有效数值
内容的提问来源于stack exchange,提问作者Dariia

