You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生产环境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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 16:01:35