Spring boot 异常处理机制
1.定义枚举
2.自定义异常类
package com.cn86trading.trading.exception; /** * Created with IntelliJ IDEA. * * @author : 小黑 * @version : 1.0 * @Project : CN86 * @Package : com.cn86trading.trading.exception * @ClassName : CustomException .java * @createTime : 2022/11/2 17:13 * @公众号 : 小黑侠 * @Description : */ public class CustomException extends RuntimeException { //异常错误编码 private int code ; //异常信息 private String message; public CustomException() { } public CustomException(CustomExceptionType customExceptionType) { this.code = customExceptionType.getCode(); this.message = customExceptionType.getDesc(); } public CustomException(CustomExceptionType customExceptionType, String message) { this.code = customExceptionType.getCode(); this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } }
3.返回类json 统一封装AjaxResponse
package com.cn86trading.trading.model; import com.cn86trading.trading.exception.CustomException; import com.cn86trading.trading.exception.CustomExceptionType; import lombok.Data; /** * Created with IntelliJ IDEA. * * @author : 小黑 * @version : 1.0 * @Project : CN86 * @Package : com.cn86trading.trading.model * @ClassName : AjaxResponse.java * @createTime : 2022/10/31 17:58 * @公众号 : 小黑侠 * @Description : */ /** * 接口数据请求统一响应数据结构 */ @Data public class AjaxResponse { private boolean isok; //请求是否处理成功 private int code; //请求响应状态码 private String message; //请求结果描述信息 private Object data; //请求结果数据(通常用于查询操作) private AjaxResponse(){} //请求出现异常时的响应数据封装 public static AjaxResponse error(CustomException e) { AjaxResponse resultBean = new AjaxResponse(); resultBean.setIsok(false); resultBean.setCode(e.getCode()); resultBean.setMessage(e.getMessage()); return resultBean; } //请求出现异常时的响应数据封装 public static AjaxResponse error(CustomExceptionType customExceptionType, String errorMessage) { AjaxResponse resultBean = new AjaxResponse(); resultBean.setIsok(false); resultBean.setCode(customExceptionType.getCode()); resultBean.setMessage(errorMessage); return resultBean; } //请求成功的响应,不带查询数据(用于删除、修改、新增接口) public static AjaxResponse success(){ AjaxResponse ajaxResponse = new AjaxResponse(); ajaxResponse.setIsok(true); ajaxResponse.setCode(200); ajaxResponse.setMessage("请求响应成功!"); return ajaxResponse; } //请求成功的响应,带有查询数据(用于数据查询接口) public static AjaxResponse success(Object obj){ AjaxResponse ajaxResponse = new AjaxResponse(); ajaxResponse.setIsok(true); ajaxResponse.setCode(200); ajaxResponse.setMessage("请求响应成功!"); ajaxResponse.setData(obj); return ajaxResponse; } //请求成功的响应,带有查询数据(用于数据查询接口) public static AjaxResponse success(Object obj,String message){ AjaxResponse ajaxResponse = new AjaxResponse(); ajaxResponse.setIsok(true); ajaxResponse.setCode(200); ajaxResponse.setMessage(message); ajaxResponse.setData(obj); return ajaxResponse; } }
测试 :
总异常异常类统一拦截器
package com.cn86trading.trading.exception; import com.cn86trading.trading.model.AjaxResponse; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * Created with IntelliJ IDEA. * * @author : 小黑 * @version : 1.0 * @Project : CN86 * @Package : com.cn86trading.trading.exception * @ClassName : WebExceptionHandler .java * @createTime : 2022/11/2 20:23 * @公众号 : 小黑侠 * @Description : */ //所有Controller异常处理 @ControllerAdvice public class WebExceptionHandler { //处理程序员主动转换的自定义异常 @ExceptionHandler(CustomException.class) @ResponseBody public AjaxResponse customerException(CustomException e) { if(e.getCode() == CustomExceptionType.SYSTEM_ERROR.getCode()){ //400异常不需要持久化,将异常信息以友好的方式告知用户就可以 //TODO 将500异常信息持久化处理,方便运维人员处理 } return AjaxResponse.error(e); } //处理程序员在程序中未能捕获(遗漏的)异常 @ExceptionHandler(Exception.class) @ResponseBody public AjaxResponse exception(Exception e) { //TODO 将异常信息持久化处理,方便运维人员处理 return AjaxResponse.error(new CustomException( CustomExceptionType.OTHER_ERROR)); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public AjaxResponse handleBindException(MethodArgumentNotValidException ex) { FieldError fieldError = ex.getBindingResult().getFieldError(); return AjaxResponse.error(new CustomException(CustomExceptionType.USER_INPUT_ERROR, fieldError.getDefaultMessage())); } @ExceptionHandler(BindException.class) @ResponseBody public AjaxResponse handleBindException(BindException ex) { FieldError fieldError = ex.getBindingResult().getFieldError(); return AjaxResponse.error(new CustomException(CustomExceptionType.USER_INPUT_ERROR, fieldError.getDefaultMessage())); } @ExceptionHandler(IllegalArgumentException.class) @ResponseBody public AjaxResponse handleIllegalArgumentException(IllegalArgumentException e) { return AjaxResponse.error( new CustomException(CustomExceptionType.USER_INPUT_ERROR, e.getMessage()) ); } }
support表示支持哪些响应类,true代表所有。只 响应APPLICATION_JSON。如果是其它类型,需要再做判断
使用@Component 和 @ControllerAdvice使类生效
package com.cn86trading.trading.exception; import com.cn86trading.trading.model.AjaxResponse; import org.springframework.core.MethodParameter; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /** * Created with IntelliJ IDEA. * * @author : 小黑 * @version : 1.0 * @Project : CN86 * @Package : com.cn86trading.trading.exception * @ClassName : GlobalResponseAdvice .java * @createTime : 2022/11/2 18:00 * @公众号 : 小黑侠 * @Description : */ @Component //监听所有Controller异常 @ControllerAdvice //实现ResponseBodyAdvice 接口的作用是:在将数据返回给用户之前,做最后一步的处理。 // 也就是说,ResponseBodyAdvice 的处理过程在全局异常处理的后面。 public class GlobalResponseAdvice implements ResponseBodyAdvice { //support表示支持哪些响应类,true代表所有。 @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { //如果响应结果是JSON数据类型 if(mediaType.equalsTypeAndSubtype( MediaType.APPLICATION_JSON)){ if(body instanceof AjaxResponse){ AjaxResponse ajaxResponse = (AjaxResponse)body; if(ajaxResponse.getCode() != 888){ //888 不是标准的HTTP状态码,特殊处理 serverHttpResponse.setStatusCode(HttpStatus.valueOf( ajaxResponse.getCode() )); } return body; }else{ serverHttpResponse.setStatusCode(HttpStatus.OK); return AjaxResponse.success(body); } } return body; } }
服务端参数校验及异常处理
第1种,使用注解进行参数校验
在实体类中使用@NotEmpty(message="文章内容不能为空")设置属性字段
@Valid 需要校验的参数前面加上该注解
异常此类抛出,然后在自定义异常中添加此异常的拦截捕获并返回。
@Valid会抛出2个异常,都需要捕获。MethodArgumentNotValidException和BindException
jsr303
第2种参数校验:利用断言的方式进行参数校验
org.springframework.util.Assert
org.springframework.util.Assert类提供很多数据校验。可以了解下
三、利用AOP完美处理页面跳转异常
如果不是返回json,比如是jsp,freemarker,thymeleaf等页面时如何跳转。
自定义AOP
最终实例:
第1步:定义注解
第2步:引入包
第3步:定义AOP切面(切入点)
第4步:定义异常类,将所有的Exception/Throwable异常类转化为RuntimeException 的ModelViewException
第5步:拦截并抛出异常之后,进行全局异常类处理。返回的是Spring 的ModelAndView 跳转到error页面setViewName
第6步:跳转到error文件,从freemarker目录中查找error.ftl文件