1.状态码抛异常
处理Web请求时引发的任何未处理的异常都会导致服务器返回HTTP 500响应。 但是,您自己编写的任何异常都可以使用@ResponseStatus注释(它支持HTTP规范定义的所有HTTP状态代码)进行注释。 当一个带注释的异常从一个控制器方法抛出,而不是在其他地方处理时,它将自动地使用指定的状态码返回适当的HTTP响应。
测试用例:
首先写一个异常处理类
@ResponseStatus(value= HttpStatus.NOT_FOUND, reason="找不到内容")
public class TdUserNotFoundException extends RuntimeException {
}
测试类加了一个注解@ResponseStatus,就是当出现这个异常的时候的返回状态。
然后写controller让其抛出TdUserNotFoundException这个异常
运行SpringBoot工程,通过swagger UI工具进行测试,测试结果为:
内容有自定义的返回码和错误信息,这样就解决了异常的问题,这样又会出现很多问题,比如必须指定抛出的异常是哪个异常,并且用哪个异常处理类去处理。
2. 单一Controller 异常处理
以在Controller中添加@ExceptionHandler注解,以专门处理由同一控制器中的请求处理(@RequestMapping)方法抛出的异常。 这样的方法可以处理没有@ResponseStatus注释的异常,不需要自定义异常了,可以将用户重定向到专用的错误视图,可以构建完全自定义的错误响应。
这个处理方法关键就是异常处理方法写在一个Controller内,当这个Controller出现异常的时候会自动调用(注意,注解一定要用@Controller)。
异常处理方法:
@ExceptionHandler(DataIntegrityViolationException.class)
public TdException conflict() {
TdException tdException = new TdException(TdResponseInfo._100100);
return tdException;
}
_100100(100100, "操作失败,数据校验失败,请检查数据"),
这个方法监控了一个DataIntegrityViolationException异常,当数据写入数据库会对内容进行校验,看看是否满足要求,比如非空校验,用户名的唯一性校验。
业务方法:
@RequestMapping("/createTdUser1")
public String createTdUser1(TdUserDto tdUserDto){
throw new DataIntegrityViolationException("1111");
}
如果上面方法出现DataIntegrityViolationException异常会被自动捕捉。
测试:
用户名admin在数据库已经有了,再次插入会报错。
3.全局异常处理
控制器建议允许您使用完全相同的异常处理技术,但将其应用于整个应用程序,而不仅仅是应用于单个控制器。 可以把它们看作是一个注解驱动的拦截器。
@RestControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler({RollbackException.class,MySQLIntegrityConstraintViolationException.class,DataIntegrityViolationException.class})
public ExceptionBody sqlException(Exception e) {
LOGGER.error("数据库异常:{}", e.getMessage(), e);
return new ExceptionBody(TdResponseInfo._100100);
}
}
同样是拦截数据库检验异常,可以专门写一个全局的异常处理类,注解使用
@RestControllerAdvice这样直接注入到@RestController里面,所有到的Contoller出现异常会被自动拦截。
同样是一个情景,当用户注册一个username就不能有相同的username,在数据库里面添加唯一性约束,然后在实体类也添加约束。
表字段
/**
* 用户名
*/
@Column(nullable = false,name = "username",length = 10,unique = true)
private String username;
Controller:
Controller方法:
@RequestMapping(value = "/createTdUser",method = RequestMethod.POST)
public RespBody<TdUser> createTdRole(TdUserDto tdUserDto){
TdUser tdUser = new TdUser();
BeanUtils.copyProperties(tdUserDto,tdUser);
if (StringUtils.isBlank(tdUser.getUsername()) || StringUtils.isBlank(tdUser.getPassword())){
logger.info("用户名或者密码为空");
return new RespBody<>(TdResponseInfo._100100,null);
}
tdUser.setSalt(tdUserDto.getUsername());
tdUser.setCreateTime(new Date());
tdUser.setModifiedTime(new Date());
tdUser.setLocked(1);
tdUser.setRemark("安存管理员");
TdUser tdUserRes = tdUserService.createTdUser(tdUser);
return new RespBody<>(TdResponseInfo._100000,tdUserRes);
}
预计TdUser tdUserRes = tdUserService.createTdUser(tdUser);行发生异常。
开始测试:
测试结果:
4.自定义异常处理
有的时候需要自定义一些已知的异常,当异常发生终止程序运行,抛出指定的返回码和返回信息。这个时候可以自定义异常拦截。
public class TdException extends RuntimeException
自定义异常处理器,注入到@RestController
@ExceptionHandler(TdException.class)
public ExceptionBody TdExceptionHandler(TdException tdException) {
return new ExceptionBody(tdException.getCode(), tdException.getMessage());
}
使用:
if (tarObjRequest == null || tarObjService.findOneById(tarObjRequest.getId()) == null){
logger.error("操作对象不存在");
logger.info("操作对象不存在");
throw new TdException(TdResponseInfo._100104);
这样就可以通过全局异常处理拦截自定义异常了。