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

Salesforce Apex REST API无法返回400/500错误响应问题排查

问题原因排查

  • 未设置实际HTTP响应状态码:你仅在自定义的ResponseWrapper里定义了StatusCode字段,没有给RestContext.response.statusCode赋值,Salesforce默认所有成功执行的请求返回200状态码,即使返回体里写了400/500,调用方拿到的HTTP状态码还是200,无法识别错误类型。
  • 异常匹配逻辑覆盖不全:当前catch块仅判断异常信息包含Validation、Internal server error字符串才返回对应状态码,实际大量错误场景不会命中该判断:比如你测试用的JSON里Date_time是带时分秒的ISO格式,用Date.valueOf()转换会直接抛类型转换异常,异常信息不含Validation字符串,根本走不到400分支。
  • 返回字段与需求不匹配:你期望的返回字段是StatusCode、id、error,但现有ResponseWrapper里的字段是StatusCode、StatusMessage、ErrorMessage、ID,字段名称、大小写都不匹配,异常场景下也未把id置空、给error字段赋值。
  • 存在空指针风险:你在try-catch块外直接执行resp.ID = caseList[0].Id,如果insert失败抛异常,caseList里的记录没有生成Id,甚至caseList可能为空,会直接触发空指针异常,导致接口返回Salesforce默认的错误页,走不到你自定义的返回逻辑。
  • 缺少入参前置校验:没有提前判断必填参数是否为空、格式是否合法,要等DML或者类型转换报错才会触发异常,很多非法请求无法及时返回400。

解决方法

修改后的完整代码

@RestResource(urlMapping='/IoT_Case__c/*')
global with sharing  class IOT_CaseManager {
    global class ResponseWrapper{
        public String StatusCode;
        public String id;
        public String error;
    }  

    @HttpPost
    global static ResponseWrapper createIOT_Case(String IOT_Case_Type,
                                   String Name,String Email,String Phone,String Facility_Name,
                                   String Address,String case_Reason,string Device_ID,
                                   String Date_time, String Pool_Name){
        RestResponse res = RestContext.response;
        ResponseWrapper resp = new ResponseWrapper(); 
        List<IoT_Case__c> caseList = new List<IoT_Case__c>();
        try{ 
            // 前置必填参数校验,直接返回400
            if(String.isBlank(IOT_Case_Type) || String.isBlank(Device_ID) || String.isBlank(Date_time)){
                resp.StatusCode = '400';
                resp.error = '必填参数IOT_Case_Type、Device_ID、Date_time不能为空';
                resp.id = '';
                res.statusCode = 400;
                return resp;
            }
            
            IoT_Case__c Iot_case = new IoT_Case__c(); 
            Iot_case.IOT_Case_Type__c = IOT_Case_Type;   
            Iot_case.Name__c = Name;
            Iot_case.Email__c = Email;     
            Iot_case.Phone__c = Phone;
            Iot_case.Address__c = Address;
            Iot_case.Pool_Name__c = Pool_Name;     
            Iot_case.Device_ID__c = Device_ID;
            Iot_case.case_Reason__c = case_Reason;
            Iot_case.Facility_Name__c = Facility_Name;
            // 单独捕获日期格式转换异常,返回400
            try{
                Iot_case.Date_time__c = Date.valueOf(Date_time);
            }catch(Exception e){
                resp.StatusCode = '400';
                resp.error = '日期格式错误,请使用yyyy-MM-dd格式';
                resp.id = '';
                res.statusCode = 400;
                return resp;
            }
            caseList.add(Iot_case);         

            // 优化重复校验逻辑,避免全表扫描
            Boolean isDuplicate = [SELECT count() FROM IoT_Case__c 
                WHERE IOT_Case_status__c IN ('Open','In -Progress') 
                AND case_Reason__c = :caseList[0].case_Reason__c 
                AND Device_ID__c = :caseList[0].Device_ID__c LIMIT 1] > 0;
            
            if(!isDuplicate){
                insert caseList; 
                resp.StatusCode = '201';
                resp.id = caseList[0].Id;
                resp.error = '';
                res.statusCode = 201;
            }else{
                resp.StatusCode = '409'; 
                resp.error = 'Duplicate case';
                resp.id = '';
                res.statusCode = 409;
            }        
        }catch (Exception e) {  
            // 自定义校验规则、字段校验异常归为400,其余运行时异常归为500
            if(e instanceof DmlException && e.getMessage().contains('FIELD_CUSTOM_VALIDATION_EXCEPTION')){
                resp.StatusCode = '400';
                resp.error = '数据校验失败:' + e.getMessage().split('FIELD_CUSTOM_VALIDATION_EXCEPTION, ')[1];
            }else{
                resp.StatusCode = '500';
                resp.error = '服务器内部错误:' + e.getMessage();
            }
            resp.id = '';
            res.statusCode = Integer.valueOf(resp.StatusCode);
        } 
        return resp; 
    } 
}

测试验证方法

  • 触发400错误:测试JSON里把Date_time改为带时分秒的"2021-02-01T03:42:22Z",或者删掉Device_ID参数,即可拿到400状态码和对应返回体。
  • 触发500错误:可在try块内故意添加除零代码Integer test = 1/0;,即可触发500状态码返回。

核心修改说明

  • 新增RestContext.response.statusCode赋值,保证HTTP响应头的状态码和返回体中的StatusCode完全一致。
  • 调整ResponseWrapper字段完全匹配需求格式。
  • 增加入参前置校验、日期格式单独校验,提前拦截非法请求返回400。
  • 优化异常分类逻辑,所有验证类异常统一归为400,其他运行时异常统一归为500,覆盖所有错误场景。
  • 把Id赋值逻辑移到insert成功的分支内,避免空指针异常。
  • 优化重复校验的SOQL逻辑,避免全表查询,防止数据量过大时触发SOQL超限错误。

内容的提问来源于stack exchange,提问作者G Reddy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 07:06:02