生产环境JS代码无法检测空对象致CRM 400异常求助
生产环境空对象绕过校验导致CRM请求400错误的排查方案
生产环境中,应用向CRM发送空请求体调用,触发400 Bad Request异常。本地模拟空对象时不会触发该问题,但生产环境下即便对象为空,updatePerson方法仍会被执行,且双重校验(lodash isEmpty + Object.keys)未拦截。Datadog日志明确显示请求体为空。
调用CRM仓库前的代码
if (!isEmpty(personDto)) { response = { status: response.status.concat(`Person has been updated : ${JSON.stringify(personDto)}`), personDto: personDto, }; await this.pipedriveRepository.updatePerson(body.current.id, personDto); } else { response = { status: response.status.concat(`Person has not changed and no update were made`), personDto: personDto, }; } return response;
updatePerson方法代码
async updatePerson(id: number, person: IPipedrivePersonDto): Promise < PipedrivePerson > { const personApi = new this.pipeDriveClient.PersonsApi(); if (isEmpty(person) || Object.keys(person).length === 0) { throw new Error(`Can't update a person with no body: ` + JSON.stringify(person)); } try { const { data } = (await personApi.updatePerson(id, person)) as PersonResponse; return data; } catch (e) { if ((e as PipedriveResponseException).context.status === 400) { return person as PipedrivePerson; } throw e; } }
Datadog日志片段
{ "id": "AQAAAYOxHSvXy25ofAAAAA4SFM3WkFBQldILXNoZVMxU2R3QUI", "content": { "timestamp": "2022-10-07T06:23:42.551Z", "tags": [ "service:growth", "filename:app.log", "sourcecategory:sourcecode", "source:nodejs", "buildpackversion:dev", "dyno:web.1", "dynotype:web", "env:prod", "version:1.0.0" ], "host": "24f45b35-3ea7-487f-9c25-2372d17ceb", "service": "growth", "message": "POST /hooks/pipedrive/persons/updated", "attributes": { "timestamp": "2022-10-07 06:23:42", "dd": { "service": "growth" }, "time": "2022-10-07T06:23:42.551Z", "response": { "body": "Person data has been cleant\r\nNo new email to validate\r\nPerson already has a valid email address\r\nPerson has been updated : {}", "status": 201 }, "level": "info" } } }
可能原因与解决思路
1. 不可枚举属性或特殊对象类型问题
Object.keys()和lodash isEmpty()仅检查可枚举的自身属性,如果personDto是类实例、Object.create(null)生成的无原型对象,或包含不可枚举属性,可能导致判断偏差。
2. 生产环境序列化/反序列化差异
生产环境中personDto可能来自外部请求、数据库等,经过序列化/反序列化后变成表面为空但存在隐藏属性的对象。
3. 异步竞态条件
判断!isEmpty(personDto)后、调用updatePerson前,personDto可能被其他异步逻辑修改为空。
修正方案
替换为更严格的空对象校验函数
覆盖所有空对象场景,包括不可枚举属性、特殊对象类型:
const isEmptyObject = (obj) => { if (obj == null) return true; // 拦截null/undefined if (typeof obj !== 'object') return true; // 非对象直接判定为空 // 检查所有自身属性(含不可枚举) return Object.getOwnPropertyNames(obj).length === 0; };
在调用处和updatePerson方法中替换原有校验:
// 调用处 if (!isEmptyObject(personDto)) { // 执行更新逻辑 } // updatePerson方法内 if (isEmptyObject(person)) { throw new Error(`Can't update a person with no body: ` + JSON.stringify(person)); }
增加生产环境调试日志
在updatePerson方法中添加详细日志,便于定位生产环境对象异常:
async updatePerson(id: number, person: IPipedrivePersonDto): Promise < PipedrivePerson > { // 生产环境保留该日志 console.log('Person debug info:', { type: typeof person, isNull: person === null, ownProperties: Object.getOwnPropertyNames(person), enumerableProperties: Object.keys(person), prototype: Object.getPrototypeOf(person), stringified: JSON.stringify(person) }); // ... 原有校验与逻辑 }
检查lodash版本一致性
确认本地与生产环境lodash版本一致,避免旧版本isEmpty的判断bug。
内容的提问来源于stack exchange,提问作者Sébastien
相关产品推荐
相关产品推荐

