前言:在做项目时,Dao和Service层的异常都会被抛到Controller层,如果我们在每个Controller的方法中都加上异常的try...catch处理就显的非常繁琐。所以springmvc为我们提供了统一的异常处理方案,可以把controller层的异常进行统一处理,这样既提高了代码的复用性也让异常处理代码和我们的业务代码解耦。
一丶通过自定义类配合@ControllerAdvice注解统一处理
1.新建自定义异常处理类,按照自己项目多种异常情况细分粒度。例如登陆异常,数据库异常等,其中@ExceptionHandler里面的value值是对应抛出时的异常类。然后封装异常信息给到前端,错误码和错误信息等。
@Slf4j
@Controller
@ControllerAdvice
public class ResultException {
@Autowired
private ConfigValueUtil configValueUtil;
@ResponseBody
@ExceptionHandler(value = LoginException.class)
public ActionResult loginException(LoginException e) {
ActionResult result = ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());
if (Boolean.parseBoolean(configValueUtil.getMultiTenancy())) {
printLoginLog(e, "登陆异常");
}
return result;
}
}
2.业务层逻辑处理,抛出对应异常。通过throw new 生成对应的异常类,传入相应异常信息。
@PostMapping("/Login")
public ActionResult<LoginVO> login(Principal principal, @RequestParam Map<String, String> parameters, @RequestBody LoginForm loginForm) throws LoginException {
TenantContextHolder.clear();
UserInfo userInfo = new UserInfo();
String phone = loginForm.getPhone();
String phoneCode = loginForm.getPhoneCode();
String timestampkey = loginForm.getTimestamp();
if(StringUtil.isNotEmpty(phone)){
List<UserEntity> userEntityList = userService.list(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getMobilePhone,phone));
if(CollectionUtils.isNotEmpty(userEntityList)){
String phoneCode1 = String.valueOf(redisUtil.getString(phone));
if("null".equals(phoneCode1)){
throw new LoginException("验证码已过期!");
}
if(!(phoneCode1.equals(phoneCode))){
throw new LoginException("验证码输入错误!");
}
}
二丶通过自定义类继承ResponseEntityExceptionHandler类,配合@ControllerAdvice注解统一处理
1.新建自定义类,继承ResponseEntityExceptionHandler类,重写handleExceptionInternal方法,构造对应的数据体返回给前端。其中handleExceptionOutputJson方法不可省略,如果要返回正确的数据体给到前端,必须构建对应的错误码,状态以及头部信息等,由于异常的底层类都是实现Throwable类,所以作为参数,方便赋值以及取值等操作。ErrorCodeAndMessageException是自定义错误信息相关的基类。
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
@ExceptionHandler({Throwable.class})
public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
return handleExceptionOutputJson(ex, request);
}
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers,
HttpStatus status, WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
return handleExceptionOutputJson(ex, request);
}
protected ResponseEntity<Object> handleExceptionOutputJson(Throwable e, WebRequest request) {
CommonResult<Object> obj = new CommonResult();
obj.setErrorCode(ErrorCode.FAILURE.getErrorCode());
obj.setMessage(e.getMessage());
if (e instanceof ErrorCodeAndMessageException) {
obj.setErrorCode(((ErrorCodeAndMessageException) e).getErrorCode());
} else if (e instanceof AccessDeniedException) {
obj.setErrorCode(ErrorCode.UNAUTHORIZED.getErrorCode());
} else if (e instanceof DuplicateKeyException) {
obj.setErrorCode(ErrorCode.DUPLICATE_RECORD.getErrorCode());
Throwable t = ((DuplicateKeyException) e).getCause();
if (t instanceof SQLIntegrityConstraintViolationException) {
obj.setMessage(((SQLIntegrityConstraintViolationException) t).getMessage());
}
} else {
obj.setErrorCode(ErrorCode.FAILURE.getErrorCode());
}
LOGGER.error("", e);
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return new ResponseEntity<>(SDJsonUtils.beanToString(obj), headers, HttpStatus.OK);
}
}
3.新建ErrorCodeAndMessageException基类,继承RuntimeException。
public class ErrorCodeAndMessageException extends RuntimeException {
private static final long serialVersionUID = 1L;
private int errorCode;
public ErrorCodeAndMessageException(ErrorCode errorCode) {
this(errorCode.getErrorCode(), errorCode.getMessage());
}
public ErrorCodeAndMessageException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
3.新建枚举类,罗列所有错误信息以及错误码,作为后续异常子类使用。
public enum ErrorCode {
SUCCESS(0, "成功"),
FAILURE(1, "失败"),
/**
* 缺少参数
*/
MISSING_PARAMETERS(1000, "缺少参数"),
/**
* 参数非法
*/
ILLEGAL_PARAMETER(1001, "参数非法"),
BAD_HANYUPINYIN_OUTPUTFORMAT_COMBINATION(1002, "拼音码输出格式非法"),
ILLEGAL_OPERATION(1003, "非法操作"),
/**
* 30000以上是数据库相关
*/
INSERT_RECORD_FAILED(30000, "插入记录失败"),
DUPLICATE_RECORD(30001, "重复的记录"),
UPDATE_RECORD_FAILED(30002, "更新记录失败"),
DELETE_RECORD_FAILED(30003, "删除记录失败"),
RECORD_DOES_NOT_EXIST(30004, "记录不存在"),
/**
* 40000以上是安全相关
*/
UNAUTHENTICATED(40000, "未认证"),
UNAUTHORIZED(40001, "未授权"),
ADMINISTRATOR_ACCOUNT_REQUIRED(40002, "需要管理员账号"),
NORMAL_ACCOUNT_REQUIRED(40003, "需要普通账号"),
USER_DOES_NOT_EXIST(40004, "用户不存在"),
WRONG_PASSWORD(40005, "密码错误"),
;
private int errorCode;
private String message;
ErrorCode(int errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}
public int getErrorCode() {
return errorCode;
}
public String getMessage() {
return message;
}
}
4.细分异常类,例如插入数据时的异常。其中就用到枚举类里面的3000异常错误。
public class InsertRecordFailedException extends ErrorCodeAndMessageException {
public InsertRecordFailedException() {
super(ErrorCode.INSERT_RECORD_FAILED);
}
public InsertRecordFailedException(String message) {
super(ErrorCode.INSERT_RECORD_FAILED.getErrorCode(), message);
}
}
5.业务层使用异常类。
public DisinfectantUseRecord createDisinfectantUseRecord(CreateDisinfectantUseRecord.Params params) {
DisinfectantUseRecord disinfectantUseRecord = new DisinfectantUseRecord();
BeanUtils.copyProperties(params, disinfectantUseRecord);
if (!StringUtils.isEmpty(disinfectantUseRecord.getLocationId())) {
Location location = locationManager.getById(disinfectantUseRecord.getLocationId());
disinfectantUseRecord.setLocation(location.getName());
}
if (!StringUtils.isEmpty(disinfectantUseRecord.getDisinfectantId())) {
Disinfectant disinfectant = disinfectantManager.getById(disinfectantUseRecord.getDisinfectantId());
disinfectantUseRecord.setDisinfectantName(disinfectant.getName());
disinfectantUseRecord.setValidDays(disinfectant.getValidDays());
}
disinfectantUseRecord.setOperator(SecurityUtils.getCurrentUserName());
int c = disinfectantUseRecordMapper.insert(disinfectantUseRecord);
if (c != 1 || disinfectantUseRecord.getId() == null) {
throw new InsertRecordFailedException();
}
return disinfectantUseRecord;
}
三丶通过实现HandlerExceptionResolver接口
1.新建自定义类实现HandlerExceptionResolver 类,重写resolveException方法
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) {
String msg = e.getMessage();
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("msg",msg);
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}