SpringMVC之JSON返回及异常处理机制
发布时间:2025-06-26 00:04       
🌟 Spring MVC JSON返回与异常处理全解析
(基于Spring Framework 5.3.x+最佳实践)
一、JSON返回核心机制
1. 基础配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter()); // JSON序列化核心
}
}
依赖要求:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version> <!-- 最新稳定版 -->
</dependency>
2. 控制器实现
@RestController // 等效于@Controller + @ResponseBody
@RequestMapping("/api")
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 自动转JSON
}
@PostMapping("/create")
public ResponseEntity<ApiResponse> createUser(@Valid @RequestBody UserDTO dto) {
User savedUser = userService.save(dto);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success(savedUser));
}
}
关键注解:
@RestController:自动序列化返回值@ResponseBody:方法级JSON响应ResponseEntity:精确控制HTTP状态码/头信息
3. 高级序列化控制
public class ApiResponse<T> {
@JsonInclude(JsonInclude.Include.NON_NULL) // 忽略null字段
private T data;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss") // 日期格式化
private LocalDateTime timestamp;
// 枚举序列化为描述文字
@JsonValue
public String getCodeDesc() {
return code.getDescription();
}
}
🔥 二、全局异常处理(工业级方案)
1. 统一异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理业务异常
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleBizEx(BusinessException ex) {
return new ErrorResponse(ex.getCode(), ex.getMessage());
}
// 处理校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public ErrorResponse handleValidEx(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
return new ErrorResponse("VALID_FAIL", errors);
}
// 兜底异常处理
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleUnknownEx(Exception ex) {
log.error("System Error: ", ex); // 记录完整堆栈
return new ErrorResponse("SYSTEM_ERROR", "服务暂不可用");
}
}
2. 自定义异常体系
public class BusinessException extends RuntimeException {
private final String errorCode;
public BusinessException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
// 带根本原因构造器
public BusinessException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
}
3. 错误响应标准化
public class ErrorResponse {
private final String code;
private final Object detail; // 支持String/List/Map
public ErrorResponse(String code, Object detail) {
this.code = code;
this.detail = detail;
}
// 生成样例:
// { "code": "AUTH_FAIL", "detail": "Token已过期" }
// { "code": "VALID_FAIL", "detail": ["邮箱格式错误", "密码强度不足"] }
}
🛡️ 三、生产环境增强策略
1. 安全敏感数据处理
@JsonFilter("sensitiveFilter")
public class UserDTO {
private String name;
@JsonIgnore // 完全排除字段
private String password;
@JsonProperty(access = Access.WRITE_ONLY) // 仅允许写入
private String authToken;
}
// 控制器动态过滤
@GetMapping("/safe-info")
public MappingJacksonValue getSafeInfo() {
User user = userService.getCurrentUser();
SimpleFilterProvider filters = new SimpleFilterProvider()
.addFilter("sensitiveFilter",
SimpleBeanPropertyFilter.serializeAllExcept("authToken"));
return new MappingJacksonValue(user).setFilters(filters);
}
2. 国际化错误消息
# errors.properties
user.not_found=用户ID {0} 不存在
email.invalid=邮箱格式不合法
@ExceptionHandler(ResourceNotFoundException.class)
public ErrorResponse handleNotFoundEx(ResourceNotFoundException ex,
Locale locale) {
String message = messageSource.getMessage(
"user.not_found",
new Object[]{ex.getResourceId()},
locale
);
return new ErrorResponse("NOT_FOUND", message);
}
3. 链路追踪集成
@ExceptionHandler(Exception.class)
public ErrorResponse handleGlobalEx(Exception ex, WebRequest request) {
String traceId = MDC.get("traceId"); // 从SLF4J MDC获取
return new ErrorResponse("SYSTEM_ERROR", traceId);
}
⚠️ 四、避坑指南
- 日期序列化问题
# application.yml spring: jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss - 循环引用解决
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Department { private List<Employee> employees; } - 空集合处理