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
相关产品推荐
相关产品推荐

