SpringBoot接收Ajax POST请求报HttpMessageNotReadableException如何解决
问题根源
- 前后端请求格式不匹配:前端实际提交的是
multipart/form-data格式的FormData数据,但强制指定了contentType: 'application/json',同时后端接口标注了consumes = "application/json",要求接收纯JSON报文。 - Spring MVC的
@RequestBody会直接读取整个请求体用Jackson做JSON反序列化,此时请求体是带multipart边界符的form-data内容,不是合法JSON,解析到开头的'-'字符时误以为是数值负号,后续没有跟数字就抛出了格式错误。
解决方案
根据是否需要上传文件可选择两种适配方案:
方案1:前端直接传纯JSON(无文件上传需求时推荐)
直接把序列化后的JSON字符串作为请求体提交,不需要用FormData包装,前端代码修改如下:
const validate = async () => { const l1 = { field1: 'l1', field2: 'l2' } const l2 = { field1: 'l3', field2: 'l4' } const list = [l1, l2]; // 直接构造请求体JSON const dataJSON = JSON.stringify({ field1: 'valu1', field2: 'valu2', field3: 'valu3', list: list }); $.ajax({ contentType: 'application/json', url: '/mypath/validate', type: 'POST', data: dataJSON, // 直接传JSON字符串 async: true, cache: false, dataType: 'json', processData: false, success: function(data, textStatus, xhr) { //Do something } }); }
该方案不需要修改后端现有代码,@RequestBody可直接正常解析DTO,参数校验逻辑也会自动生效。
补充注意:你当前DTO使用了分组校验,如果要按指定分组校验,需要把Controller方法参数上的
@Valid替换为@Validated({L1.class, L2.class, L3.class}),才能触发对应分组的校验规则。
方案2:保留FormData提交(有文件上传需求时使用)
如果需要同时上传表单文件必须使用FormData,就修改后端适配逻辑:
- 去掉接口的
consumes = "application/json"配置,改为接收multipart/form-data请求 - 用
@RequestParam接收JSON字符串,再手动反序列化为DTO,同时手动触发校验,代码示例:
@Controller @SessionScope @RequestMapping(value = "/mypath") @Validated public class ValidateController { @Autowired private Validator validator; @PostMapping(value = "/validate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @ResponseBody public ResponseEntity<?> validate(@RequestParam("request") String requestJson) throws JsonProcessingException { // 手动反序列化JSON ObjectMapper objectMapper = new ObjectMapper(); RequestDTO requestDTO = objectMapper.readValue(requestJson, RequestDTO.class); // 手动触发参数校验 Set<ConstraintViolation<RequestDTO>> violations = validator.validate(requestDTO); if (!violations.isEmpty()) { // 自定义处理校验异常返回 return ResponseEntity.badRequest().body(violations.stream().map(ConstraintViolation::getMessage).toList()); } return ResponseEntity.ok().body(requestDTO); } }
内容的提问来源于stack exchange,提问作者Maikel
相关产品推荐
相关产品推荐

