一、需求
实际项目中,经常抛出各种异常,不能直接抛出异常给前端,这样用户体验相当不好,用户看不懂你的Exception,对于一些sql异常,直接抛到页面上也不安全。所以有没有好的办法解决这些问题呢,当然有了,进行全局异常处理,当正常返回给前端就行了。
例如:
二、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
三、创建统一返回结果封装类
import lombok.Data;
@Data
public class Response<T> {
private int code;
private String msg;
private T data;
public Response() {
}
public Response(int code) {
this.code = code;
}
public Response(int code, String msg) {
this.code = code;
this.msg = msg;
}
public Response(T data) {
this.data = data;
}
public Response(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static Response ok() {
Response response = new Response();
response.setCode(200);
response.setMsg("成功");
return response;
}
public static <T> Response<T> ok(T data) {
Response response = new Response();
response.setCode(200);
response.setMsg("成功");
response.setData(data);
return response;
}
public static Response fail(String msg) {
Response response = new Response();
response.setCode(201);
response.setMsg(msg);
return response;
}
public static Response fail(int code, String msg) {
Response response = new Response();
response.setCode(code);
response.setMsg(msg);
return response;
}
}
四、创建状态码常量类
我这里直接用了基本数据类型做状态码,没有写描述,也可以用map的形式创建对象,既有状态码,又有描述。
public class HttpStatusData {
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
}
五、编写全局异常类
import com.alibaba.fastjson.JSONException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.ResourceAccessException;
import java.nio.file.AccessDeniedException;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* @author HTT
*/
@RestControllerAdvice
@Slf4j
public class ExceptionAdviceHandler {
/**
* 权限校验异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(AccessDeniedException.class)
public Response handleAccessDeniedException(AccessDeniedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},权限校验失败{}", requestURI, e.getMessage());
return Response.fail(HttpStatusData.FORBIDDEN, "没有权限,请联系管理员授权");
}
/**
* 请求方式不支持
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Response handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},不支持{}请求", requestURI, e.getMethod());
return Response.fail(e.getMessage());
}
/**
* 参数验证失败异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
String message = e.getBindingResult().getFieldError().getDefaultMessage();
log.error("请求地址{},参数验证失败{}", requestURI, e.getObjectName(), e);
return Response.fail(message);
}
/**
* 拦截错误SQL异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(BadSqlGrammarException.class)
public Response handleBadSqlGrammarException(BadSqlGrammarException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生数据库异常.", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "数据库异常!");
}
/**
* 可以拦截表示违反数据库的完整性约束导致的异常。
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(DataIntegrityViolationException.class)
public Response handleDataIntegrityViolationException(DataIntegrityViolationException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生数据库异常.", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "数据库异常!");
}
/**
* 可以拦截违反数据库的非完整性约束导致的异常,可能也会拦截一些也包括 SQL 语句错误、连接问题、权限问题等各种数据库异常。
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(UncategorizedSQLException.class)
public Response handleUncategorizedSqlException(UncategorizedSQLException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生数据库异常.", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "数据库异常!");
}
/**
* 拦截未知的运行时异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(RuntimeException.class)
public Response handleRuntimeException(RuntimeException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},发生未知运行异常", requestURI, e);
return Response.fail(e.getMessage());
}
/**
* 业务自定义异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(BusinessException.class)
public Response handleServiceException(BusinessException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
Integer code = 201;
log.error("请求地址{},发生业务自定义异常{}", requestURI, e.getMessage(), e);
// return code != null ? Response.fail(code, e.getMessage()) : Response.fail(e.getMessage());
return Response.fail(e.getMessage());
}
/**
* 全局异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(Exception.class)
public Response handleException(Exception e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},发生系统异常", requestURI, e);
return Response.fail(e.getMessage());
}
/**
* 发生数据唯一性约束异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public Response sQLIntegrityConstraintViolationException(RuntimeException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},发生数据唯一性约束异常", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "数据库异常!,发生数据唯一性约束异常");
}
/**
* 网络请求超时异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(ResourceAccessException.class)
public Response resourceAccessException(RuntimeException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},网络请求超时异常", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "网络请求超时异常,调用三方接口异常");
}
/**
* 网络请求超时异常
*
* @param e
* @param request
* @return
*/
@ExceptionHandler(JSONException.class)
public Response jSONException(RuntimeException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址{},JSON格式转换异常", requestURI, e);
return Response.fail(HttpStatusData.ERROR, "JSON格式转换异常");
}
}
六、创建自定义异常类
/**
* @author: wr
* @date: 2020/2/17 18:03
*/
public class BusinessException extends RuntimeException {
/**
* 无参构造函数
*/
public BusinessException(){
super();
}
/**
* 指定异常信息构造函数
* @param message
*/
public BusinessException(String message){
super(message);
}
}
七、 编写测试类
@RestController
@RequestMapping
public interface EntrepreneurPortraitApi {
@PostMapping("/test")
public void test(){
throw new BusinessException("测试全局异常是否生效");
}
}
八、测试结果
启动项目,浏览器访问 http://localhost:8888/test
{
"code": 201,
"msg": "失败",
"data": "测试全局异常是否生效"
}