Spring boot 异常配置
Posted xiaodujava
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot 异常配置相关的知识,希望对你有一定的参考价值。
文章目录
Spring boot 异常配置
上一节 Spring boot 参数校验
源码
直接上代码。。。
1.通过ExceptionHandler 处理控制器异常
1. 局部异常处理
在controller中通过注解@ExceptionHandler 修饰控制器的方法,即可处理本控制器的异常信息;
属性value 指定处理那些异常
@RestController
@RequestMapping("ex")
@Api(tags = {"测试局部异常处理"})
public class TestPartExceptionContorller {
@GetMapping("ex3")
@ApiOperation(value = "测试局部异常处理", httpMethod = "GET")
public void testPartException() throws Exception {
int i = 1 / 0; // 模拟程序报错
}
/*局部异常处理,只处理当前controller的异常信息*/
@ExceptionHandler(value = {Exception.class})
public ResponseEntity<ResultBean<Void>> handlerPartException(HttpServletRequest request, HttpServletResponse response, Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ResultBean.result(HttpStatus.INTERNAL_SERVER_ERROR));
}
}
当 请求 访问到testPartException方法时报错后,我们在handlerPartException 指定了处理Exception的异常,所以报错Exception以及子类异常都会走异常处理方法。
通过@ExceptionHandler 和@RestControllerAdvice (或者@ControllerAdvice) 做全局异常处理
全局默认异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
/*默认异常处理*/
@ExceptionHandler(value = {Exception.class})
public ResultBean<Void> defExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod method) throws Exception {
String message = e.getMessage();
log.error("error -- message = " + message, e);
return ResultBean.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
}
...
通过@RestControllerAdvice 指定本类为异常处理类,另@RestControllerAdvice 的value 或者basePackages 属性可以指定具体那个包下控制器;
方法defExceptionHandler为默认异常处理(因为我们指定了Exception,异常类的基类),如果程序报异常会找到指定的ControllerAdvice中的 标注了@ExceptionHandler方法有哪些能够处理改异常的方法,若没有完全匹配的则找父类一级一级想上找;最终肯定就会找到 Exception 匹配的方法处理。
上一章节 Spring boot 参数校验; 我们进行了参数校验的学习;但是返回的信息不太友好;我们这章通过全局异常处理对校验后的异常进行处理封装,同时也熟悉了全局异常处理的使用方式。
参数校验异常处理
处理MethodArgumentNotValidException 和 ConstraintViolationException 异常信息
这两个异常会在参数校验出错的时候抛出;
MethodArgumentNotValidException: 是实体bean校验出错
ConstraintViolationException: 是基本参数类型(包括string) 校验出错
/*valid 参数校验异常处理*/
//MethodArgumentNotValidException ConstraintViolationException
@ExceptionHandler(value = {ConstraintViolationException.class, MethodArgumentNotValidException.class})
public ResponseEntity<ResultBean<Void>> constraintValidatedExceptionHandler(HttpServletRequest request, Exception e, HandlerMethod method) {
ResponseEntity.BodyBuilder responseEntityBuilder = ResponseEntity.badRequest();
ResultBean<Void> resultBean = ResultBean.result(HttpStatus.BAD_REQUEST);
StringJoiner stringJoiner = new StringJoiner(";", "异常信息{ ", " }");
if (e instanceof ConstraintViolationException) {
ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
Optional.ofNullable(constraintViolationException.getConstraintViolations()).orElseGet(HashSet::new)
.forEach(v -> {
String message = v.getMessage();
PathImpl propertyPath = (PathImpl) v.getPropertyPath();
String s = propertyPath.getLeafNode().asString();
stringJoiner.add(s + ": " + message);
});
}
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
List<FieldError> fieldErrors = methodArgumentNotValidException.getBindingResult().getFieldErrors();
Optional.ofNullable(fieldErrors).orElseGet(ArrayList::new).forEach(v -> {
String field = v.getField();
String defaultMessage = v.getDefaultMessage();
stringJoiner.add(field + ": " + defaultMessage);
});
}
resultBean.setMsg(stringJoiner.toString());
return responseEntityBuilder.body(resultBean);
}
我们还是使用swagger 进行http调用请求;
请求获取商品详情; 指定了id 的长度
@ApiOperation(value = "获取商品详情", httpMethod = "GET")
@GetMapping("product")
public Product getProduct(@Length(max = 32, min = 32) @RequestParam String id) throws Exception {
Product product = new Product();
product.setId(id);
product.setName("xiaomi 11");
product.setPrice(199900L);
return product;
}
通过swagger 请求;
指定id 为11 不满足参数校验; 不满足会 报异常ConstraintViolationException
返回信息
code 为400; msg 也是我们封装的信息
请求添加商品信息
对Product 进行了参数校验;
Product
@ApiModel(description = "商品详情")
public class Product {
@ApiModelProperty(required = true, value = "商品名称")
@NotBlank
@Length(min = 1, max = 500)
private String name;
@ApiModelProperty(required = true, value = "商品id")
@NotBlank
@Length(max = 32, min = 32)
private String id;
@ApiModelProperty(required = true, value = "商品价格,以分为单位", dataType = "java.lang.Long", example = "1000")
@Max(Integer.MAX_VALUE)
@Min(0)
private Long price;
@ApiModelProperty(required = true, value = "生产日期", dataType = "date")
@NotNull
private Date pdTime;
请求方法
@ApiOperation(value = "添加商品信息", httpMethod = "POST")
@PostMapping("product")
public String addProduct(@Validated @RequestBody Product product) throws Exception {
return product.getId();
}
swagger 访问,还是让id的长度不够32; 对java bean的校验这里会报错 MethodArgumentNotValidException 异常
返回信息
处理HttpMessageNotReadableException 异常
也是属请求参数的异常信息;当参数类型不对或者解析出错时 会报错HttpMessageNotReadableException; 例如 String转 Integer 传入的参数不是数字,还有Date类型format出错;
/*参数转换异常处理*/
@ExceptionHandler(value = {HttpMessageNotReadableException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public HttpEntity<ResultBean<Void>> methodArgumentNotValidExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e, HandlerMethod method) {
String localizedMessage = e.getLocalizedMessage();
ResultBean<Void> resultBean = ResultBean.result(HttpStatus.BAD_REQUEST);
HttpEntity<ResultBean<Void>> httpEntity = new HttpEntity(resultBean);
if (localizedMessage.startsWith("JSON parse error")) {
String through_reference_chain = localizedMessage.substring(localizedMessage.lastIndexOf("through reference chain"));
String field = through_reference_chain.substring(through_reference_chain.lastIndexOf("[\\"") + 2, through_reference_chain.lastIndexOf("\\"]"));
resultBean.setMsg("参数解析出错!请检测参数: " + field);
}
return httpEntity;
}
如上我们还可以通过 @ResponseStatus 指定 http状态码;
swagger 访问我们把日期格式输入错误; pdTime 默认格式应该为 yyyy-MM-dd HH:mm:ss.SSS格式;我们使用yyyy/MM/dd格式传输;
返回信息
自定义异常处理
创建业务异常类BusinessException; 处理自定义异常BusinessException
/*自定义异常处理*/
@ExceptionHandler(value = {BusinessException.class})
public void businessExceptionHandler(HttpServletRequest request, HttpServletResponse response, BusinessException e, HandlerMethod method) throws IOException {
log.error("异常处理: businessException;\\n【 远端请求地址:{}; 请求URI:{};" +
"\\n 请求方法:{}; error message:{}】",
request.getRemoteHost(), request.getRequestURI(),
method.getMethod().getName(), e.getLocalizedMessage());
response.getWriter().write(new ObjectMapper().writeValueAsString(ResultBean.error(e.getCode(), e.getMsg())));
}
请求方法访问,抛出一个 BusinessException 异常
@GetMapping("ex")
@ApiOperation(value = "测试自定义异常处理", httpMethod = "GET")
public Object testMyException(@RequestParam(required = false) @ApiParam(name = "str", value = "str") String str) throws Exception {
throw new BusinessException("test businessException", 5001);
}
swagger 访问;处理自定义异常; 略。。。
总结
上面的示例基本包含了常用的处理方式,@ExceptionHandler 修饰的方法 我们可以返回一个具体的ReusltBean; 或者一个HttpEntity 以及 ResponseEntity; 甚至一个void;
2.通过继承ResponseEntityExceptionHandler 进行全局异常处理
@RestControllerAdvice
public class GlobalExHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
...
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
...
}
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
...
}
...
}
ResponseEntityExceptionHandler中的handleException 处理大部分的异常进行;也是通过 @ExcepitonHandler
@ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
...
所以继承该类 重新指定异常方法即可~
下一节 Spring boot lombok
以上是关于Spring boot 异常配置的主要内容,如果未能解决你的问题,请参考以下文章
解决Myeclipse启动Spring Boot项目报出莫名异常
Spring boot:thymeleaf 没有正确渲染片段