Spring中使用PATCH传递多类型JSON参数时类型不匹配如何解决?
解决PATCH请求中类型不匹配导致的IllegalArgumentException问题
问题根源
JSON反序列化时,所有数字会默认解析为Integer类型,但你的GradeEto中subjectEntityId是Long类型,直接通过ReflectionUtils赋值时,Integer无法直接转换为Long,触发类型不匹配异常。同理其他类型不匹配场景也会出现该问题。
解决方案
方案1:手动添加类型转换逻辑
在反射赋值前,根据目标字段类型对传入值做转换,适配字段类型:
@Override public GradeEto partialUpdate(Long id, Map<String, Object> updateInfo) { Grade grade = gradeRepository.findById(id).get(); GradeEto gradeeto = GradeMapper.mapToETO(grade); updateInfo.forEach((key, value) -> { Field field = ReflectionUtils.findField(GradeEto.class, key); if (field != null) { field.setAccessible(true); Class<?> targetType = field.getType(); Object convertedValue = convertValue(value, targetType); ReflectionUtils.setField(field, gradeeto, convertedValue); } }); // 后续保存及返回逻辑... return gradeeto; } // 类型转换工具方法 private Object convertValue(Object value, Class<?> targetType) { if (value == null || targetType.isAssignableFrom(value.getClass())) { return value; } // 处理数字类型转换 if (Number.class.isAssignableFrom(targetType)) { Number number = (Number) value; if (targetType == Long.class || targetType == long.class) { return number.longValue(); } else if (targetType == Integer.class || targetType == int.class) { return number.intValue(); } else if (targetType == Double.class || targetType == double.class) { return number.doubleValue(); } } // 可扩展其他类型转换,如String转LocalDateTime等 return value; }
方案2:使用专用Patch DTO接收参数
放弃Map<String, Object>,定义包含允许修改字段的DTO,让Spring自动完成类型转换,同时实现编译期类型校验:
// 定义Patch专用DTO public class GradePatchDto { private Integer value; private String comment; private Long subjectEntityId; // 生成getter、setter方法 } // 修改Controller方法 @PatchMapping("/grades/{id}") public GradeEto partialUpdate(@PathVariable("id") final Long id, @RequestBody GradePatchDto updateInfo) { return gradeService.partialUpdate(id, updateInfo); } // 修改Service逻辑 @Override public GradeEto partialUpdate(Long id, GradePatchDto updateInfo) { Grade grade = gradeRepository.findById(id).get(); GradeEto gradeEto = GradeMapper.mapToETO(grade); // 仅更新非空字段 if (updateInfo.getValue() != null) { gradeEto.setValue(updateInfo.getValue()); } if (updateInfo.getComment() != null) { gradeEto.setComment(updateInfo.getComment()); } if (updateInfo.getSubjectEntityId() != null) { gradeEto.setSubjectEntityId(updateInfo.getSubjectEntityId()); } // 后续保存及返回逻辑... return gradeEto; }
方案3:遵循REST标准使用JsonPatch
使用Spring提供的JsonPatch工具,符合RFC 6902规范处理PATCH请求:
import org.springframework.data.rest.webmvc.json.JsonPatch; import org.springframework.data.rest.webmvc.json.JsonPatchException; import org.springframework.data.rest.webmvc.json.MappingJacksonValue; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @PatchMapping(path = "/grades/{id}", consumes = "application/json-patch+json") public GradeEto partialUpdate(@PathVariable("id") final Long id, @RequestBody JsonPatch patch) { Grade grade = gradeRepository.findById(id).get(); GradeEto gradeEto = GradeMapper.mapToETO(grade); try { MappingJacksonValue jacksonValue = new MappingJacksonValue(gradeEto); GradeEto patchedEto = patch.apply(jacksonValue, new MappingJackson2HttpMessageConverter().getObjectMapper()); // 转换为实体并保存 Grade patchedGrade = GradeMapper.mapToEntity(patchedEto); gradeRepository.save(patchedGrade); return patchedEto; } catch (JsonPatchException e) { throw new RuntimeException("Patch操作失败", e); } }
此时前端需发送application/json-patch+json类型请求,格式示例:
[ {"op": "replace", "path": "/value", "value": 2}, {"op": "replace", "path": "/comment", "value": "Test Idea"}, {"op": "replace", "path": "/subjectEntityId", "value": 2} ]
方案选择
- 若需保留现有反射逻辑:选择方案1,快速适配类型转换
- 追求类型安全与代码可读性:优先方案2,明确允许修改的字段,避免反射风险
- 遵循REST标准规范:选择方案3,支持更丰富的PATCH操作
内容的提问来源于stack exchange,提问作者essaPL
相关产品推荐
相关产品推荐

