Spring Boot 统一返回格式 + 全局异常处理(实战版)
在Spring Boot项目开发中,接口返回格式混乱、异常处理分散是新手常踩的坑——不同接口返回不同结构的数据,前端解析繁琐;异常直接抛出到前端,暴露系统细节且不友好;重复编写返回结果封装代码,冗余低效。
本文结合实战场景,手把手实现「统一返回格式」+「全局异常处理」,整合Spring Boot内置的异常处理机制,规范接口返回标准,统一捕获项目中所有异常(自定义异常、系统异常、业务异常),让接口开发更规范、维护更高效,同时兼顾前端解析体验和系统安全性。
前置说明:本文基于Spring Boot 2.7.x版本开发,适配RESTful接口规范,可直接整合到之前的「Spring Boot + MyBatis-Plus」项目中,无缝衔接单表CRUD接口。
一、核心目标(为什么要做统一返回和全局异常)
在没有统一处理的情况下,项目接口会存在以下问题:
-
返回格式混乱:成功时返回实体对象,失败时返回字符串/错误码,前端需单独处理每种情况,开发效率低;
-
异常暴露敏感信息:系统报错(如空指针、数据库异常)时,直接返回堆栈信息,可能泄露数据库地址、表结构等敏感内容;
-
异常处理分散:每个接口都要try-catch捕获异常,代码冗余,且容易遗漏异常场景;
-
前端对接成本高:无统一的错误码规范,前端无法快速判断异常类型(如参数错误、权限不足、系统异常)。
因此,我们需要实现两个核心目标:
-
统一返回格式:无论接口成功/失败,都返回固定结构的JSON数据,包含状态码、提示信息、业务数据;
-
全局异常处理:集中捕获项目中所有异常,统一封装异常信息,隐藏敏感细节,返回规范的错误响应。
二、第一步:实现统一返回格式
统一返回格式的核心是「封装一个通用返回实体类」,搭配「静态工具方法」,简化接口返回代码,确保所有接口返回结构一致。
1. 定义返回状态码枚举(规范错误码)
先定义一个枚举类,管理所有接口的状态码和对应提示信息,避免硬编码错误码,便于后续维护(可根据项目需求扩展)。
package com.example.cruddemo.common;
/**
* 统一返回状态码枚举
* 规范:200=成功,4xx=客户端错误,5xx=服务端错误,自定义错误码从1000开始
*/
public enum ResultCode {
// 成功状态
SUCCESS(200, "操作成功"),
// 客户端错误(4xx)
PARAM_ERROR(400, "参数错误"),
NOT_FOUND(404, "资源不存在"),
METHOD_NOT_ALLOWED(405, "请求方法不允许"),
// 服务端错误(5xx)
SYSTEM_ERROR(500, "系统异常,请联系管理员"),
DB_ERROR(501, "数据库操作异常"),
// 自定义业务错误(1000+)
USER_NOT_EXIST(1001, "用户不存在"),
USER_NAME_DUPLICATE(1002, "用户名已存在"),
PASSWORD_ERROR(1003, "密码错误");
// 状态码
private final Integer code;
// 提示信息
private final String msg;
// 构造方法
ResultCode(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
// getter方法(无需setter,枚举值不可修改)
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
2. 封装通用返回实体类
定义一个Result类,作为所有接口的统一返回载体,包含3个核心字段:状态码(code)、提示信息(msg)、业务数据(data),搭配静态工具方法,快速构建返回结果。
package com.example.cruddemo.common;
import lombok.Data;
import java.io.Serializable;
/**
* 统一返回格式实体类
* 所有接口返回结果都封装为此类,确保格式统一
* Serializable:实现序列化,便于跨服务传输(可选,单体项目可省略)
*/
@Data
public class Result<T> implements Serializable {
// 状态码(对应ResultCode枚举)
private Integer code;
// 提示信息
private String msg;
// 业务数据(泛型T,适配不同类型的返回数据:实体、列表、字符串等)
private T data;
// 私有构造方法,禁止外部直接new(通过静态方法构建)
private Result() {}
// 私有构造方法,用于快速赋值
private Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
// -------------------------- 静态工具方法(快速构建返回结果)--------------------------
/**
* 成功返回(无业务数据)
*/
public static <T> Result<T> success() {
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), null);
}
/**
* 成功返回(有业务数据)
* @param data 业务数据(泛型,可传实体、列表等)
*/
public static <T> Result<T> success(T data) {
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
}
/**
* 成功返回(自定义提示信息+业务数据)
* @param msg 自定义提示信息
* @param data 业务数据
*/
public static <T> Result<T> success(String msg, T data) {
return new Result<>(ResultCode.SUCCESS.getCode(), msg, data);
}
/**
* 失败返回(根据ResultCode枚举)
* @param resultCode 状态码枚举
*/
public static <T> Result<T> fail(ResultCode resultCode) {
return new Result<>(resultCode.getCode(), resultCode.getMsg(), null);
}
/**
* 失败返回(根据ResultCode枚举+自定义提示信息)
* @param resultCode 状态码枚举
* @param msg 自定义提示信息(覆盖枚举默认msg)
*/
public static <T> Result<T> fail(ResultCode resultCode, String msg) {
return new Result<>(resultCode.getCode(), msg, null);
}
/**
* 失败返回(自定义状态码+提示信息)
* @param code 自定义状态码
* @param msg 提示信息
*/
public static <T> Result<T> fail(Integer code, String msg) {
return new Result<>(code, msg, null);
}
}
3. 接口改造(使用统一返回格式)
修改之前的UserController接口,将所有接口的返回值改为Result,通过静态工具方法构建返回结果,替代原来的字符串、实体类返回,实现格式统一。
package com.example.cruddemo.controller;
import com.example.cruddemo.common.Result;
import com.example.cruddemo.common.ResultCode;
import com.example.cruddemo.entity.User;
import com.example.cruddemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
// 1. 新增用户(POST请求)
@PostMapping("/add")
public Result<Void> addUser(@RequestBody User user) {
// 简单业务校验(后续可通过全局异常处理优化)
if (user.getName() == null || user.getName().isEmpty()) {
// 失败返回:参数错误
return Result.fail(ResultCode.PARAM_ERROR, "用户名不能为空");
}
boolean success = userService.save(user);
return success ? Result.success() : Result.fail(ResultCode.DB_ERROR, "新增用户失败");
}
// 2. 根据ID查询用户(GET请求)
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
User user = userService.getById(id);
// 若用户不存在,返回自定义业务错误
if (user == null) {
return Result.fail(ResultCode.USER_NOT_EXIST);
}
// 成功返回,携带用户数据
return Result.success(user);
}
// 3. 查询所有用户(GET请求)
@GetMapping("/list")
public Result<List<User>> getUserList() {
List<User> userList = userService.list();
// 成功返回,携带用户列表数据
return Result.success(userList);
}
// 4. 根据ID修改用户(PUT请求)
@PutMapping("/update")
public Result<Void> updateUser(@RequestBody User user) {
if (user.getId() == null) {
return Result.fail(ResultCode.PARAM_ERROR, "用户ID不能为空");
}
boolean success = userService.updateById(user);
return success ? Result.success() : Result.fail(ResultCode.DB_ERROR, "修改用户失败");
}
// 5. 根据ID删除用户(DELETE请求)
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
boolean success = userService.removeById(id);
return success ? Result.success() : Result.fail(ResultCode.DB_ERROR, "删除用户失败");
}
}
4. 统一返回格式效果演示
改造后,所有接口返回格式完全统一,前端可根据code判断接口状态,无需适配多种返回结构:
-
成功返回(有数据):
{ "code": 200, "msg": "操作成功", "data": { "id": 1, "name": "张三", "age": 22, "email": "zhangsan@163.com", "createTime": "2024-05-20 10:30:00", "updateTime": "2024-05-20 10:30:00" } } -
成功返回(无数据):
{ "code": 200, "msg": "操作成功", "data": null } -
失败返回(参数错误):
{ "code": 400, "msg": "用户名不能为空", "data": null } -
失败返回(业务错误):
{ "code": 1001, "msg": "用户不存在", "data": null }
三、第二步:实现全局异常处理
统一返回格式解决了“返回结构混乱”的问题,但接口中仍可能抛出异常(如空指针、数据库异常、自定义业务异常)。全局异常处理的核心是「集中捕获所有异常」,通过Spring Boot提供的@RestControllerAdvice和@ExceptionHandler注解,无需在每个接口中try-catch,自动封装异常为统一返回格式。
1. 编写全局异常处理器
创建GlobalExceptionHandler类,添加@RestControllerAdvice注解(标识为全局异常处理器,适配RESTful接口),然后通过@ExceptionHandler注解,针对不同类型的异常,编写对应的处理方法,实现“不同异常,不同响应”。
package com.example.cruddemo.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import java.sql.SQLException;
/**
* 全局异常处理器
* 集中捕获项目中所有异常,统一封装返回格式,避免异常直接暴露给前端
* @RestControllerAdvice:等同于@ControllerAdvice + @ResponseBody,返回JSON格式异常响应
* @Slf4j:Lombok注解,用于打印异常日志(便于排查问题)
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// -------------------------- 1. 自定义业务异常(最常用,手动抛出)--------------------------
/**
* 捕获自定义业务异常(BusinessException)
* 业务中需要手动抛出异常时,使用此类,如:throw new BusinessException(ResultCode.USER_NOT_EXIST);
*/
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
// 打印异常日志(仅打印提示信息,不打印堆栈,避免日志冗余)
log.warn("业务异常:{}", e.getMessage());
// 封装异常信息,返回统一格式
return Result.fail(e.getCode(), e.getMessage());
}
// -------------------------- 2. 系统异常(无需手动抛出,自动捕获)--------------------------
/**
* 捕获数据库异常(SQLException)
* 如:数据库连接失败、SQL语法错误、主键冲突等
*/
@ExceptionHandler(SQLException.class)
public Result<Void> handleSQLException(SQLException e) {
// 打印异常堆栈(系统异常,需要详细日志排查问题)
log.error("数据库异常:", e);
// 隐藏敏感信息,返回统一的数据库异常提示
return Result.fail(ResultCode.DB_ERROR);
}
/**
* 捕获空指针异常(NullPointerException)
* 项目中最常见的系统异常,统一捕获,避免返回堆栈信息
*/
@ExceptionHandler(NullPointerException.class)
public Result<Void> handleNullPointerException(NullPointerException e) {
log.error("空指针异常:", e);
return Result.fail(ResultCode.SYSTEM_ERROR, "系统异常,请联系管理员");
}
/**
* 捕获通用系统异常(Exception)
* 兜底异常处理器,捕获所有未被上面单独处理的异常(避免遗漏)
*/
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常:", e);
return Result.fail(ResultCode.SYSTEM_ERROR);
}
// -------------------------- 3. 请求参数异常(客户端传入参数错误)--------------------------
/**
* 捕获参数校验异常(MethodArgumentNotValidException)
* 配合@Valid注解使用,用于校验请求参数(如:用户名不能为空、年龄必须大于0)
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
// 获取参数校验失败的详细信息
BindingResult bindingResult = e.getBindingResult();
// 拼接所有错误提示(如:用户名不能为空;年龄必须大于0)
StringBuilder errorMsg = new StringBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorMsg.append(fieldError.getDefaultMessage()).append(";");
}
// 截取最后一个“;”,避免格式冗余
String msg = errorMsg.substring(0, errorMsg.length() - 1);
log.warn("参数校验异常:{}", msg);
return Result.fail(ResultCode.PARAM_ERROR, msg);
}
/**
* 捕获参数类型不匹配异常(MethodArgumentTypeMismatchException)
* 如:接口要求传入Long类型的ID,但客户端传入了字符串
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public Result<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
String msg = String.format("参数类型不匹配:%s 应为 %s 类型", e.getName(), e.getRequiredType().getSimpleName());
log.warn(msg);
return Result.fail(ResultCode.PARAM_ERROR, msg);
}
// -------------------------- 4. HTTP请求异常(客户端请求方式/路径错误)--------------------------
/**
* 捕获404异常(资源不存在)
*/
@ExceptionHandler(org.springframework.web.servlet.NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) // 设置HTTP响应状态码为404
public Result<Void> handleNoHandlerFoundException(org.springframework.web.servlet.NoHandlerFoundException e) {
log.warn("资源不存在:{}", e.getRequestURL());
return Result.fail(ResultCode.NOT_FOUND);
}
/**
* 捕获405异常(请求方法不允许)
*/
@ExceptionHandler(org.springframework.web.HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) // 设置HTTP响应状态码为405
public Result<Void> handleHttpRequestMethodNotSupportedException(org.springframework.web.HttpRequestMethodNotSupportedException e) {
String msg = String.format("请求方法不允许:%s,支持的方法:%s", e.getMethod(), e.getSupportedHttpMethods());
log.warn(msg);
return Result.fail(ResultCode.METHOD_NOT_ALLOWED, msg);
}
}
2. 编写自定义业务异常类
系统异常(如空指针、数据库异常)会自动抛出,但业务异常(如用户不存在、用户名重复)需要我们手动抛出。编写BusinessException类,继承RuntimeException,配合全局异常处理器,实现业务异常的统一捕获。
package com.example.cruddemo.common;
import lombok.Getter;
/**
* 自定义业务异常类
* 业务中需要抛出异常时,使用此类(如:用户不存在、密码错误)
* 继承RuntimeException:无需强制try-catch,交给全局异常处理器自动捕获
*/
@Getter // 提供getter方法,全局异常处理器需要获取code和msg
public class BusinessException extends RuntimeException {
// 异常状态码(对应ResultCode枚举)
private final Integer code;
// 异常提示信息
private final String message;
// 构造方法1:传入ResultCode枚举,直接获取code和msg
public BusinessException(ResultCode resultCode) {
this.code = resultCode.getCode();
this.message = resultCode.getMsg();
}
// 构造方法2:传入ResultCode枚举 + 自定义提示信息(覆盖枚举默认msg)
public BusinessException(ResultCode resultCode, String message) {
this.code = resultCode.getCode();
this.message = message;
}
// 构造方法3:自定义code和msg(不使用枚举,灵活适配特殊场景)
public BusinessException(Integer code, String message) {
this.code = code;
this.message = message;
}
// 重写getMessage方法,返回自定义提示信息
@Override
public String getMessage() {
return message;
}
}
3. 开启404异常捕获(可选)
默认情况下,Spring Boot不会将404异常(资源不存在)抛给全局异常处理器,需要在application.yml中添加配置,开启404异常捕获,确保404异常也能返回统一格式。
# 开启404异常捕获,交给全局异常处理器处理
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false # 关闭静态资源映射(避免静态资源404被拦截)
4. 优化接口:手动抛出业务异常
修改UserController接口,删除原来的if-else参数校验,改用「@Valid注解校验参数」+「手动抛出BusinessException」,简化代码,同时让异常处理更集中。
package com.example.cruddemo.controller;
import com.example.cruddemo.common.BusinessException;
import com.example.cruddemo.common.Result;
import com.example.cruddemo.common.ResultCode;
import com.example.cruddemo.entity.User;
import com.example.cruddemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@RestController
@RequestMapping("/api/user")
@Validated // 开启参数校验(用于方法参数单独校验,如@PathVariable、@RequestParam)
public class UserController {
@Autowired
private UserService userService;
// 1. 新增用户(POST请求)- 优化:参数校验+手动抛异常
@PostMapping("/add")
public Result<Void> addUser(@Valid @RequestBody User user) {
// 业务校验:用户名是否重复(模拟业务逻辑)
boolean exists = userService.lambdaQuery().eq(User::getName, user.getName()).exists();
if (exists) {
// 手动抛出业务异常,全局异常处理器会自动捕获并封装返回格式
throw new BusinessException(ResultCode.USER_NAME_DUPLICATE);
}
userService.save(user);
return Result.success();
}
// 2. 根据ID查询用户(GET请求)- 优化:手动抛异常
@GetMapping("/{id}")
public Result<User> getUserById(@NotNull(message = "用户ID不能为空") @PathVariable Long id) {
User user = userService.getById(id);
if (user == null) {
throw new BusinessException(ResultCode.USER_NOT_EXIST);
}
return Result.success(user);
}
// 3. 查询所有用户(GET请求)
@GetMapping("/list")
public Result<List<User>> getUserList() {
return Result.success(userService.list());
}
// 4. 根据ID修改用户(PUT请求)- 优化:参数校验
@PutMapping("/update")
public Result<Void> updateUser(@Valid @RequestBody User user) {
userService.updateById(user);
return Result.success();
}
// 5. 根据ID删除用户(DELETE请求)- 优化:参数校验
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@NotNull(message = "用户ID不能为空") @PathVariable Long id) {
userService.removeById(id);
return Result.success();
}
// 新增:根据姓名查询用户(参数校验示例)
@GetMapping("/list/name")
public Result<List<User>> getUserByName(@NotBlank(message = "用户名不能为空") @RequestParam String name) {
List<User> userList = userService.lambdaQuery().eq(User::getName, name).list();
return Result.success(userList);
}
}
5. 实体类参数校验(配合@Valid注解)
在User实体类中添加参数校验注解(如@NotBlank、@NotNull),配合接口中的@Valid注解,实现请求参数的自动校验,无需手动编写if-else校验逻辑。
package com.example.cruddemo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.util.Date;
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
// 用户名:不能为空,长度1-30
@NotBlank(message = "用户名不能为空")
@Length(min = 1, max = 30, message = "用户名长度必须在1-30之间")
private String name;
// 年龄:不能为null,且必须为正数
@NotNull(message = "年龄不能为空")
@Positive(message = "年龄必须为正数")
private Integer age;
// 邮箱:格式校验
@Email(message = "邮箱格式不正确")
private String email;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
四、全局异常处理效果演示
配置完成后,所有异常都会被全局异常处理器捕获,自动封装为统一返回格式,隐藏敏感信息,同时打印详细日志便于排查问题,以下是常见异常的响应效果:
1. 业务异常(用户不存在)
请求接口:GET /api/user/999(ID=999的用户不存在),手动抛出BusinessException,响应如下:
{
"code": 1001,
"msg": "用户不存在",
"data": null
}
日志打印(仅打印提示信息,不打印堆栈):warn: 业务异常:用户不存在
2. 参数校验异常(用户名不能为空)
请求接口:POST /api/user/add(传入name=null),@Valid注解校验失败,响应如下:
{
"code": 400,
"msg": "用户名不能为空",
"data": null
}
日志打印:warn: 参数校验异常:用户名不能为空
3. 参数类型不匹配异常
请求接口:GET /api/user/abc(ID要求Long类型,传入字符串abc),响应如下:
{
"code": 400,
"msg": "参数类型不匹配:id 应为 Long 类型",
"data": null
}
4. 系统异常(空指针)
接口中出现空指针异常(如user.getEmail().length(),user为null),响应如下(隐藏堆栈信息):
{
"code": 500,
"msg": "系统异常,请联系管理员",
"data": null
}
日志打印(打印完整堆栈,便于排查):error: 空指针异常:java.lang.NullPointerException: ...
5. 404异常(资源不存在)
请求接口:GET /api/user/abc123(接口路径不存在),响应如下:
{
"code": 404,
"msg": "资源不存在",
"data": null
}
五、高频踩坑点汇总(实战必避)
踩坑1:全局异常处理器不生效
✅ 报错原因:1. 类未添加@RestControllerAdvice注解;2. 异常处理方法未添加@ExceptionHandler注解,或注解中指定的异常类型错误;3. 全局异常处理器类未被Spring Boot扫描到(包路径与启动类不一致)。
✅ 解决方案:确保添加@RestControllerAdvice和@ExceptionHandler注解;核对异常类型(如捕获自定义异常,需传入BusinessException.class);确保全局异常处理器的包路径在启动类扫描范围内(如启动类包是com.example.cruddemo,处理器包是com.example.cruddemo.common)。
踩坑2:404异常无法被捕获
✅ 报错原因:未在application.yml中添加开启404异常捕获的配置,Spring Boot默认不抛出404异常。
✅ 解决方案:添加配置spring.mvc.throw-exception-if-no-handler-found=true和spring.web.resources.add-mappings=false。
踩坑3:参数校验注解(@Valid)不生效
✅ 报错原因:1. 接口方法未添加@Valid注解;2. 实体类未添加参数校验注解(如@NotBlank);3. 控制层类未添加@Validated注解(用于方法参数单独校验,如@PathVariable);4. 未引入参数校验依赖(Spring Boot 2.7.x已内置,无需额外引入)。
✅ 解决方案:接口参数添加@Valid注解,实体类添加对应校验注解,控制层类添加@Validated注解。
踩坑4:自定义异常抛出后,全局处理器未捕获
✅ 报错原因:1. 自定义异常未继承RuntimeException(继承Exception会强制try-catch,无法自动抛出);2. @ExceptionHandler注解中指定的异常类型错误(如写成Exception.class,但实际抛出的是BusinessException)。
✅ 解决方案:自定义异常继承RuntimeException;确保@ExceptionHandler(BusinessException.class)指定的异常类型与抛出的异常一致。
踩坑5:返回格式不统一(部分接口未使用Result类)
✅ 报错原因:忘记修改部分接口的返回值,仍返回实体类、字符串等,未封装为Result。
✅ 解决方案:统一检查所有接口,确保返回值都是Result类型,通过Result.success()/Result.fail()构建返回结果。
六、实战优化建议(贴合企业开发)
-
错误码规范:严格遵循枚举类中的错误码规则(200=成功,4xx=客户端错误,5xx=服务端错误,1000+=业务错误),避免乱编错误码,便于前后端对接和问题排查;
-
日志规范:业务异常打印warn级别日志(仅提示信息),系统异常打印error级别日志(完整堆栈),避免日志冗余或缺失;
-
参数校验:尽量使用@Valid+校验注解,替代手动if-else校验,简化代码,提升可读性;
-
异常分类:按“自定义业务异常→参数异常→系统异常→兜底异常”的顺序编写处理器方法,兜底异常(Exception)放在最后,避免覆盖其他异常;
-
扩展场景:可在Result类中添加timestamp(时间戳)字段,便于前端记录请求时间;可添加traceId(链路追踪ID),用于分布式项目的异常链路排查。
七、总结
Spring Boot 统一返回格式 + 全局异常处理,是接口开发的“标准化配置”,核心价值在于「规范、高效、安全」:
-
统一返回格式:通过Result类和ResultCode枚举,让所有接口返回结构一致,降低前端对接成本;
-
全局异常处理:通过@RestControllerAdvice和@ExceptionHandler,集中捕获所有异常,避免重复try-catch,隐藏系统敏感信息;
-
实战适配:无缝衔接MyBatis-Plus、RESTful接口,可直接整合到现有项目,同时提供踩坑点和优化建议,贴合企业开发规范。
本文实现的方案,兼顾了简洁性和实用性,新手可直接复制代码使用,后续可根据项目需求(如分布式、多模块),扩展错误码、日志链路追踪等功能。掌握这套方案,能让你的Spring Boot接口开发更规范、更高效,避免因返回格式混乱、异常处理不当导致的问题。
2578

被折叠的 条评论
为什么被折叠?



