SpringBoot 接口开发中,「接口能调用,但返回结果异常」是最常见的问题之一------前端请求正常发起,后端无报错,但前端拿到的是乱码、JSON解析失败、状态码错误(406/415)、返回格式混乱,排查起来一头雾水,尤其前后端分离项目,接口返回异常直接导致页面渲染失败、功能卡壳。
✅ JSON解析失败:Could not read JSON document: Unexpected character; ✅ 接口响应乱码:返回中文变成???、饼饿; ✅ 状态码错误:406 Not Acceptable、415 Unsupported Media Type; ✅ 返回格式异常:本该返回JSON,却返回String/HTML; ✅ 日期格式化异常:返回时间戳/乱码日期,前后端格式不统一; ✅ 空值返回异常:null字段被过滤/返回null导致前端报错。
本篇把 SpringBoot 接口返回的 8大高频异常 一次性讲透,每个异常都配「报错原文+核心根因+可直接复制的解决方案+避坑技巧」,覆盖JSON解析、响应乱码、状态码、日期格式化等全场景,新手照着改就能解决,老手也能查漏补缺,建议收藏,接口开发时直接对照!
一、前置基础:接口返回正常的核心条件
SpringBoot 接口默认返回JSON格式(依赖spring-boot-starter-web),要保证返回正常,需满足3个核心条件:
-
引入 spring-boot-starter-web 依赖(自动集成Jackson JSON解析器);
-
接口方法添加 @ResponseBody 注解(或类上添加 @RestController,等同于@Controller+@ResponseBody);
-
返回对象(POJO/Map/List)可被Jackson正常序列化,无特殊字符、无循环依赖。
关键提醒:接口返回异常,前端报错往往比后端更明显(如控制台提示JSON解析失败),可结合前后端报错一起排查,效率更高!
二、8大高频接口返回异常+解决方案(按出现概率排序)
场景1:接口响应乱码(中文变成???、饼饿)
1. 典型现象/报错
接口返回中文乱码,前端显示为???、饼饿等乱码字符;后端控制台打印正常,仅响应给前端时乱码。
// 后端返回预期
{"msg":"操作成功","code":200,"data":"测试中文"}
// 前端实际接收
{"msg":"????","code":200,"data":"饼饿ä¸ÂÃ¥ÂÂ"}
2. 核心原因
-
SpringBoot 默认响应编码为 ISO-8859-1(不支持中文),未配置为 UTF-8;
-
接口返回String类型时,未指定响应编码;
-
配置文件中字符集配置错误,未全局指定UTF-8。
3. 解决方案(3种任选,优先方案1,全局生效)
# 方案1:yml全局配置(推荐,一次配置,全部接口生效)
spring:
http:
encoding:
charset: UTF-8
force: true # 强制覆盖默认编码
enabled: true # 开启编码配置
# 方案2:接口单独配置(针对单个接口)
@RestController
@RequestMapping("/user")
public class UserController {
// 单独指定响应编码和返回格式
@GetMapping(value = "/get", produces = "application/json;charset=UTF-8")
public Result getUser() {
return Result.success("测试中文");
}
}
# 方案3:配置消息转换器(解决复杂乱码场景)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 配置Jackson JSON转换器,指定UTF-8编码
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setDefaultCharset(StandardCharsets.UTF_8);
// 注册转换器
converters.add(0, converter);
}
}
场景2:JSON解析失败(Could not read JSON document)
1. 典型报错原文
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON document: Unexpected character ('}' (code 125)): was expecting double-quote to start field name
at [Source: (PushbackInputStream); line: 3, column: 5] (through reference chain: com.xxx.dto.UserDTO["name"]); nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name
2. 核心原因
-
前端传递的JSON格式错误(如字段未加双引号、逗号遗漏、括号不匹配);
-
前后端数据类型不匹配(如后端接收Integer,前端传String;后端接收Date,前端传非标准时间格式);
-
后端实体类缺少无参构造、getter/setter方法,Jackson无法解析;
-
JSON中包含Jackson无法序列化的类型(如LocalDateTime未配置格式化)。
3. 解决方案
-
校验前端JSON格式:确保字段用双引号、逗号不遗漏、括号匹配(可通过JSON在线校验工具验证);
-
后端实体类规范(必做):
// 正确实体类:有无参构造、getter/setter、toString @Data // Lombok注解,自动生成getter/setter/无参构造 public class UserDTO { private Long id; private String name; private Integer age; // 若有LocalDateTime字段,需配置格式化 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime; } // 注意:Lombok的@Data注解会自动生成无参构造,若手动写了有参构造,需手动添加无参构造 @NoArgsConstructor // 手动添加无参构造 @AllArgsConstructor // 有参构造 @Data public class UserDTO { // 字段... } -
配置LocalDateTime全局格式化(解决日期解析失败):
@Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { // 全局配置LocalDateTime格式 builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") )); builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") )); }; } }场景3:状态码406 Not Acceptable(无法接受的响应格式)
1. 典型报错原文
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]2. 核心原因
前端请求头中指定了「Accept」格式(如Accept: text/html),但后端接口返回的是JSON格式,两者不匹配;或后端接口无法返回前端要求的格式。
常见场景:前端请求头Accept设为text/plain,后端返回JSON;或后端接口返回String,前端要求JSON格式。
3. 解决方案
-
统一前后端响应格式:前端请求头Accept设为 application/json(推荐,前后端分离默认);
-
后端接口指定返回格式(强制返回JSON):
// 方法上添加produces,指定返回JSON格式 @GetMapping(value = "/getUser", produces = "application/json;charset=UTF-8") public Result getUser() { return Result.success(userService.getUserById(1L)); } -
排查后端接口返回值:确保返回的是POJO/Map/List(可被Jackson序列化为JSON),避免直接返回String(若返回String,需指定produces为text/plain)。
-
场景4:状态码415 Unsupported Media Type(不支持的媒体类型)
1. 典型报错原文
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
2. 核心原因
前端传递的请求体格式(Content-Type)与后端接口要求的格式不匹配,常见场景:
3. 解决方案
四、生产避坑指南(接口开发必记)
五、总结
SpringBoot 接口返回异常,90% 都是 编码配置错误、JSON格式不规范、序列化配置缺失、前后端约定不一致 这四类问题。核心解决思路是:确保前后端请求/响应格式一致,Jackson能正常序列化返回对象,编码统一为UTF-8。
遇到接口返回异常,无需慌乱,按「查前端请求→查后端注解→查序列化配置→查后端日志」的顺序排查,对照本文对应场景修改,就能快速解决。
本文涵盖了接口开发中所有高频返回异常,从基础配置到生产优化,从临时解决到根本规避,新手照着做就能落地,老手也能查漏补缺。
如果这篇文章帮到你了,记得点赞+收藏🌟!评论区说说你遇到过的接口返回异常,一起交流避坑经验~
-
后端接口用 @RequestBody 接收JSON参数,但前端Content-Type设为 application/x-www-form-urlencoded;
-
前端传递JSON格式,但Content-Type未设为 application/json;
-
后端接口不支持前端传递的Content-Type(如前端传multipart/form-data,后端未配置文件上传)。
3. 解决方案
-
统一前后端请求格式:
-
后端用 @RequestBody 接收参数 → 前端Content-Type设为 application/json,传递JSON格式;
-
后端用 @RequestParam 接收参数 → 前端Content-Type设为 application/x-www-form-urlencoded;
-
-
前端请求示例(正确传递JSON):
// 前端axios请求示例 axios({ url: "/user/add", method: "post", headers: { "Content-Type": "application/json" // 必须指定 }, data: { name: "张三", age: 20 } })后端接口示例(正确接收JSON):
@PostMapping("/add") public Result addUser(@RequestBody UserDTO userDTO) { // @RequestBody接收JSON userService.addUser(userDTO); return Result.success(); }场景5:日期返回异常(时间戳/乱码/格式不统一)
1. 典型现象
接口返回的日期字段为时间戳(如1699999999999)、乱码(如2024-05-20T10:30:00),或前后端日期格式不统一(后端返回yyyy-MM-dd,前端需要yyyy/MM/dd)。
2. 核心原因
Jackson默认对LocalDateTime、Date类型的序列化规则,与前端预期不一致;未配置全局日期格式化,导致日期返回格式混乱。
3. 解决方案(两种任选,优先全局配置)
# 方案1:全局日期格式化(推荐,所有日期字段生效) @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); // 配置Date类型格式化 objectMapper.registerModule(new JavaTimeModule()); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 配置LocalDateTime类型格式化 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper; } } # 方案2:单个字段格式化(针对特定字段) @Data public class UserDTO { private Long id; private String name; // 单个字段指定格式 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime; @JsonFormat(pattern = "yyyy-MM-dd") private Date birthday; }场景6:接口返回null字段被过滤(前端拿不到null值)
1. 典型现象
后端返回的对象中部分字段为null,但前端接收的JSON中没有该字段,导致前端取值报错(如Cannot read property 'xxx' of undefined)。
2. 核心原因
Jackson默认配置会过滤掉值为null的字段,或项目中配置了「过滤null字段」的规则,导致null字段不返回。
3. 解决方案(两种任选)
# 方案1:全局配置,保留null字段 @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); // 保留null字段(默认是过滤null) objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); return objectMapper; } } # 方案2:单个类/字段配置,保留null字段 // 单个类:所有字段保留null @JsonInclude(JsonInclude.Include.ALWAYS) @Data public class UserDTO { private Long id; private String name; private String address; // 若为null,会返回该字段 } // 单个字段:仅该字段保留null @Data public class UserDTO { private Long id; private String name; @JsonInclude(JsonInclude.Include.ALWAYS) private String address; }场景7:接口本该返回JSON,却返回String/HTML
1. 典型现象
后端接口返回的是POJO对象,但前端接收的是String字符串(如"com.xxx.dto.UserDTO@123456"),或HTML页面(如404页面)。
2. 核心原因
-
接口未加 @ResponseBody 注解,或类未加 @RestController 注解(SpringMVC默认返回视图,而非JSON);
-
接口返回值为Object类型,且未被Jackson序列化(如直接返回Object对象,未指定具体类型);
-
确保接口正确添加注解:
// 方式1:类上添加@RestController(推荐) @RestController // 等同于@Controller + @ResponseBody @RequestMapping("/user") public class UserController { @GetMapping("/get") public Result getUser() { return Result.success(userService.getUserById(1L)); } } // 方式2:方法上添加@ResponseBody @Controller @RequestMapping("/user") public class UserController { @GetMapping("/get") @ResponseBody // 必须添加,否则返回视图 public Result getUser() { return Result.success(userService.getUserById(1L)); } }-
统一接口返回格式:所有接口返回统一的Result对象(避免直接返回POJO/Object);
-
配置全局异常处理,返回JSON格式错误信息(避免返回HTML):
@RestControllerAdvice // 全局异常处理 public class GlobalExceptionHandler { // 处理所有异常,返回JSON格式 @ExceptionHandler(Exception.class) public Result handleException(Exception e) { return Result.fail(500, e.getMessage()); } }场景8:JSON序列化循环依赖(Could not write JSON document)
1. 典型报错原文
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON document: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)]2. 核心原因
两个实体类互相引用(如User类有OrderList字段,Order类有User字段),Jackson序列化时陷入循环,导致栈溢出。
3. 解决方案
使用 @JsonIgnore 注解,忽略循环引用的字段(避免序列化时陷入循环):
// User类 @Data public class User { private Long id; private String name; // 忽略OrderList字段,避免循环序列化 @JsonIgnore private List<Order> orderList; } // Order类 @Data public class Order { private Long id; private String orderNo; // 忽略User字段,避免循环序列化 @JsonIgnore private User user; }补充:也可使用 @JsonManagedReference 和 @JsonBackReference 注解,实现双向引用的正常序列化(适合需要保留部分引用字段的场景)。
三、接口返回异常万能排查步骤(4步定位问题)
-
查前端请求:检查请求头(Content-Type、Accept)是否正确,JSON格式是否规范;
-
查后端注解:确认接口类加@RestController、方法加@ResponseBody,返回值为可序列化对象;
-
查序列化配置:确认Jackson配置正确,日期格式化、null字段处理、循环依赖已解决;
-
查后端日志:查看控制台是否有序列化异常、参数解析异常,定位具体报错字段。
-
-
全局配置优先:统一配置UTF-8编码、日期格式化、Jackson序列化规则,避免单个接口重复配置;
-
前后端约定一致:提前约定请求格式(Content-Type)、响应格式(JSON)、日期格式,避免不匹配;
-
实体类规范:所有POJO类必须有无参构造、getter/setter方法,避免Jackson序列化失败;