一、使用场景
在单体微服务项目中,平时我们在编码时,出现的异常不能直接返回给客户端,问题直接暴露给前台,前台使用体验很差,而且网站还可能遭到恶意攻击。因此使用全局异常捕获,自定义返回结果很有必要。
二、实现全局异常解析器
2.1 引入依赖
基于本文实现的解析器引入相关依赖
<!-- 参数校验依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.2 代码实现
1.工具类(返回给前端的结果封装)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class R {
/**
* 通用成功状态码
*/
public static final String SUCCESS_CODE = "001";
/**
* 失败状态码
*/
public static final String FAIL_CODE = "004";
/**
* 未登录
*/
public static final String USER_NO_LOGIN = "401";
private String code;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String msg;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Object data;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long total;
/**
* 成功
* @param msg
* @param data
* @return
*/
public static R ok(String msg,Object data,Long total){
return new R(SUCCESS_CODE,msg,data,total);
}
/**
* 成功
* @param data
* @return
*/
public static R ok(String msg,Object data){
return ok(msg,data,null);
}
/**
* 成功
* @return
*/
public static R ok(String msg){
return ok(msg,null);
}
/**
* 成功
* @return
*/
public static R ok(Object data){
return ok(null,data);
}
/**
* 失败
* @param msg
* @param data
* @return
*/
public static R fail(String msg,Object data,Long total){
return new R(FAIL_CODE,msg,data,total);
}
/**
* 失败
* @param data
* @return
*/
public static R fail(String msg,Object data){
return fail(msg,data,null);
}
/**
* 失败
* @return
*/
public static R fail(String msg){
return fail(msg,null);
}
/**
* 失败
* @return
*/
public static R fail(Object data){
return fail(null,data);
}
/**
* 未登录
* @return
*/
public static R NO_LOGIN(){
return fail(USER_NO_LOGIN,"用户未登录!");
}
}
2.全局异常解析器
@Slf4j
@SuppressWarnings("ALL")
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.OK)
public R bindException(HttpServletRequest request,
HttpServletResponse response,
BindException exception) {
BindingResult bindingResult = exception.getBindingResult();
//获取所有校验异常信息进行拼接返回
//获取所有校验异常信息进行拼接返回
String message = exception.getBindingResult().getFieldError().getDefaultMessage();
return R.fail(message);
// return R.fail(exception.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
public R methodArgumentNotValidException(HttpServletRequest request,
HttpServletResponse response,
MethodArgumentNotValidException exception) {
BindingResult bindingResult = exception.getBindingResult();
log.info("GlobalExceptionHandler类中methodArgumentNotValidException方法异常,异常参数:"+exception.getBindingResult().getFieldError().getDefaultMessage());
//获取所有校验异常信息进行拼接返回
String message = exception.getBindingResult().getFieldError().getDefaultMessage();
return R.fail(message);
}
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.OK)
public R methodArgumentNotValidException(HttpServletRequest request,
HttpServletResponse response,
MissingServletRequestParameterException exception) {
// return R.fail(exception.getMessage());
log.info("GlobalExceptionHandler类中methodArgumentNotValidException方法异常,异常信息:"+exception.getMessage());
return R.fail("请正确填入参数!");
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.OK)
public R methodArgumentNotValidException(HttpServletRequest request,
HttpServletResponse response,
ConstraintViolationException exception) {
System.out.println(exception.getLocalizedMessage());
Iterator<ConstraintViolation<?>> iterator = exception.getConstraintViolations().iterator();
if (iterator.hasNext()) {
ConstraintViolationImpl next = (ConstraintViolationImpl)iterator.next();
return R.fail(next.getMessage());
}
return R.fail(exception.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.OK)
public R exception(HttpServletRequest request,
HttpServletResponse response,
Exception exception) {
log.info("GlobalExceptionHandler类中exception异常,异常信息:"+exception.getMessage());
return R.fail("预期之外的异常!");
}
}
3.添加异常解析器
若上述异常解析器不够用。现在我们业务中出现新的需求,需要使用shiro实现登录,那么shiro在登录失败时会返回一个AuthenticationException异常,此时我们在解析器中添加shiro验证异常解析。
/**
* shiro登录验证失败异常解析器
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.OK)
public R methodAuthenticationException(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) {
log.info("GlobalExceptionHandler类中methodAuthenticationException方法异常,异常信息:"+exception.getMessage());
return R.fail("登陆失败,请检查账号密码是否正确!");
}