-
Session 认证和 Token 认证
-
过滤器和拦截器
-
SpringBoot统一返回和统一异常处理
上篇文章我们学习了基于 Token
认证的登录功能实现,分别使用了过滤器和拦截器去实现登录功能,这篇文章我们来学习项目中常用的统一返回结果和统一异常处理。
一、统一返回结果
前后端分离时代,如果没有一个统一的数据返回格式,前后端调试时,前端开发人员会骂娘的,同时约定相同的返回接口数据也有助于高效的工作。
通常统一返回的格式包含三部分:
code
:状态码,一般200
表示正常message
:状态码对应的描述。data
:返回的数据
1.1、统一返回对象
新建一个 SpringBoot
项目定义通用的响应对象
package com.laoxiang.utils;import com.fasterxml.jackson.annotation.JsonInclude;import java.io.Serializable;/*** @author db* @version 1.0* @description ResponseResult* @since 2023/7/12*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> implements Serializable {private static final long serialVersionUID = 2233637474601103587L;// 接口响应状态码private Integer code;// 接口响应信息private String msg;// 接口响应的数据private T data;public ResponseResult() {this.code = AppHttpCodeEnum.SUCCESS.getCode();this.msg = AppHttpCodeEnum.SUCCESS.getMsg();}public ResponseResult(Integer code, T data) {this.code = code;this.data = data;}public ResponseResult(Integer code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}public ResponseResult(Integer code, String msg) {this.code = code;this.msg = msg;}public static ResponseResult errorResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.error(code, msg);}public static ResponseResult okResult() {ResponseResult result = new ResponseResult();return result;}public static ResponseResult okResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.ok(code, null, msg);}public static ResponseResult okResult(Object data) {ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg());if (data != null) {result.setData(data);}return result;}public static ResponseResult errorResult(AppHttpCodeEnum enums) {return setAppHttpCodeEnum(enums, enums.getMsg());}public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg) {return setAppHttpCodeEnum(enums, msg);}public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums) {return okResult(enums.getCode(), enums.getMsg());}private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg) {return okResult(enums.getCode(), msg);}public ResponseResult<?> error(Integer code, String msg) {this.code = code;this.msg = msg;return this;}public ResponseResult<?> ok(Integer code, T data) {this.code = code;this.data = data;return this;}public ResponseResult<?> ok(Integer code, T data, String msg) {this.code = code;this.data = data;this.msg = msg;return this;}public ResponseResult<?> ok(T data) {this.data = data;return this;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}
1.2、系统常量
上面代码中提到了常量 AppHttpCodeEnum
,定义自己的应用程序特定状态码,来表示具体的情况。通过定义的状态码就可以知道具体代表什么意思。
package com.laoxiang.utils;/*** @author db* @version 1.0* @description AppHttpCodeEnum* @since 2023/7/12*/
public enum AppHttpCodeEnum {// 成功SUCCESS(200,"操作成功"),// 失败ERROR(500,"操作失败"),int code;String msg;AppHttpCodeEnum(int code, String errorMessage){this.code = code;this.msg = errorMessage;}public int getCode() {return code;}public String getMsg() {return msg;}
}
1.3、web
层统一响应结果
下面的例子,可以看到在实际项目中接口返回值。
二、统一异常处理
程序开发中不可避免的会遇到异常现象,如果不进行处理,遇到异常时,开发人员不能清晰地处理问题,或者使用 try{...}catch{...}
代码块进行处理,但是满屏的 try{...}catch{...}
代码块造成代码过于臃肿。
有没有更好的处理方式呢?全局统一异常处理应运而生。@ControllerAdvice
注解搭配 @ExceptionHandler
进行全局统一异常处理。
2.1、@ControllerAdvice
注解
@ControllerAdvice
注解:用于声明一个全局控制器Advice
,相当于把@ExceptionHandler
、@InitBinder
和@ModelAttribute
注解的方法集中到一个地方。放在特定类上,被认为是全局异常处理器。
2.2、ExceptionHandler
注解
- 用于定义异常处理方法,处理特定类型的异常。放在全局异常处理器类中的具体方法上。
通过这两个注解的配合,可以实现全局的异常处理。当控制器中抛出异常时,SpringBoot
会自动调用匹配的@ExceptionHandler
方法来处理异常,并返回定义的响应。
步骤如下:
- 新建一个统一异常处理类
- 类上标注
@RestControllerAdvice
注解 - 在方法上标注
@ExceptionHandler
注解,并且指定需要捕获的异常,可以同时捕获多个。
新建 exception
包,在包下新建 GlobalExceptionHandler
类。代码如下:
package com.duan.execption;import com.duan.pojo.AppHttpCodeEnum;
import com.duan.pojo.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** @author db* @version 1.0* @description GlobalExceptionHandler* @since 2023/7/23*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {// 全局异常@ExceptionHandler(Exception.class)public ResponseResult exceptionHandler(Exception e){//打印异常信息log.error("出现了异常! {}",e);//从异常对象中获取提示信息封装返回return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(),e.getMessage());}// 自定义异常@ExceptionHandler(LxAdminException.class)public ResponseResult LxAdminExceptionHandler(LxAdminException e){//打印异常信息log.error("出现了异常! {}",e);//从异常对象中获取提示信息封装返回return ResponseResult.errorResult(e.getCode(),e.getMsg());}}
在exception
包下新建自定义异常处理类 LxAdminException
package com.duan.execption;import com.duan.pojo.AppHttpCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author db* @version 1.0* @description LxAdminException 自定义异常* @since 2023/7/23*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LxAdminException extends RuntimeException{private int code;private String msg;public LxAdminException(AppHttpCodeEnum httpCodeEnum) {super(httpCodeEnum.getMsg());this.code = httpCodeEnum.getCode();this.msg = httpCodeEnum.getMsg();}
}
2.3、统一异常处理使用
在业务开发中,可以在 service
层处理业务时,可以手动抛出异常,由全局异常处理器处理进行统一处理。
package com.duan.controller;import com.duan.execption.LxAdminException;
import com.duan.pojo.AppHttpCodeEnum;
import com.duan.pojo.ResponseResult;
import com.duan.pojo.User;
import com.duan.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.UUID;/*** @author db* @version 1.0* @description LoginController* @since 2023/12/19*/
@RestController
@Slf4j
public class LoginController {@PostMapping("/login")public ResponseResult login(@RequestBody User user){log.info("这是正常日志");if(!"admin".equals(user.getUsername()) && !"123456".equals(user.getPassword())){throw new LxAdminException(AppHttpCodeEnum.LOGIN_ERROR);}else{HashMap<String, Object> map = new HashMap<>();map.put("id", UUID.randomUUID().toString());// 生成tokenString token = JWTUtils.generateJwt(map);return ResponseResult.okResult(token);}}}
当我们请求接口时,假如用户名称或者密码错误,接口就会响应:
{"code": 505,"msg": "用户名或密码错误","data": null
}
实际开发中还有许多的异常需要捕获,比如 Token
失效、过期等异常, 如果整合了其他的框架,还要注意这些框架抛出的异常,比如Spring Security
等框架。
代码地址:https://gitee.com/duan138/practice-code/tree/dev/resultException
三、总结
在 SpringBoot
项目中,统一返回和统一异常处理是非常常用的一环,它们能提高应用的可读性和可维护性,统一返回有助于保持代码一致性和规范性,在前后端联调时更加方便,统一异常处理,减少了代码冗余,对异常处理更加易于管理。
下一篇我们来看看 SpringBoot
项目中怎么进行日志处理。
改变你能改变的,接受你不能改变的,关注公众号:程序员康康,一起成长,共同进步。