GeekTop
  • 首页
  • 关于

GeekTop

不止代码

Spring Boot是如何处理异常的

Spring Boot是如何处理异常的

2021年10月3日 Alex Comments 0 Comment

恰当的异常处理是保证程序健壮不可或缺的部分。Spring提供了多种方式来处理异常。

Java的异常逻辑

众所周知,Java中的异常是派生于Throwable类的一个实例,分为Checked Exception和Unchecked Exception,如下图所示:

所有从RuntimeException中继承的异常,属于Unchecked Exception,是程序的逻辑错误,例如NullPointException,ArrayIndexOutOfBoundsException异常等。因此,只要出现RuntimeException,那一定是你的问题。

Error类型的异常,通常是Java运行时内部出现的错误和资源耗尽的错误,我们编写的代码不应该抛出这种类型的错误。

Checked Exception,表示程序本身没有问题,但是由于IO错误导致的异常,这种异常是必须要处理的。

Spring异常处理机制

Spring中要处理的通常是RuntimeException的子类,我们通常不去主动捕获这些异常,而在程序的某一个位置统一处理。Spring Boot为我们提供了一系列的注解,用来处理这些异常:

  • @ResponseStatus
  • @ExceptionHandler
  • @ControllerAdvice

如果我们不启用这些注解,Spring处理异常通常有默认的行为逻辑,抛出的异常通常会报500错误。

@ResponseStatus允许我们去修改HTTP的返回码,我们可以把这个注解用在异常类上:

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NoSuchElementFoundException extends RuntimeException {
  ...
}

抛出这个异常会改变HTTP的返回码,如下所示:

{
  "timestamp": "2020-11-29T09:42:12.287+00:00",
  "status": 404,
  "error": "Not Found",
  "message": "Item with id 1 not found",
  "path": "/product/1"
} 

我们还可以通过继承ResponseStatusException的方式来修改HTTP的返回码:

public class NoSuchElementFoundException extends ResponseStatusException {

  public NoSuchElementFoundException(String message){
    super(HttpStatus.NOT_FOUND, message);
  }

  @Override
  public HttpHeaders getResponseHeaders() {
      // return response headers
  }
}

此时,还可以通过getResponseHeaders方法来修改返回的头部。

@ExceptionHandler注解可以修改返回的数据类型,可以用在Controller里面或者@ControllerAdvice注解修饰的类里面。

@RestController
@RequestMapping("/product")
public class ProductController { 

  private final ProductService productService;

  //constructor omitted for brevity...

  @GetMapping("/{id}")
  public Response getProduct(@PathVariable String id) {
    return productService.getProduct(id);
  }

  @ExceptionHandler(NoSuchElementFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public ResponseEntity<String> handleNoSuchElementFoundException(
      NoSuchElementFoundException exception
  ) {
    return ResponseEntity
        .status(HttpStatus.NOT_FOUND)
        .body(exception.getMessage());
  }
}

@ControllerAdvice如下所示;

@RestControllerAdvice
public class ApiExceptionHandler {
  @ExceptionHandler(MissingServletRequestParameterException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handlerError(MissingServletRequestParameterException e) {
    String message = String.format("Missing Request Parameter: %s", e.getParameterName());
    return BaseResponse
      .builder()
      .message(message)
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(MethodArgumentTypeMismatchException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(MethodArgumentTypeMismatchException e) {
    log.warn("Method Argument Type Mismatch", e);
    String message = String.format("Method Argument Type Mismatch: %s", e.getName());
    return BaseResponse
      .builder()
      .message(message)
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(MethodArgumentNotValidException e) {
    log.warn("Method Argument Not Valid", e);
    BindingResult result = e.getBindingResult();
    FieldError error = result.getFieldError();
    String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
    return BaseResponse
      .builder()
      .message(message)
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(BindException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(BindException e) {
    log.warn("Bind Exception", e);
    FieldError error = e.getFieldError();
    assert error != null;
    String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
    return BaseResponse
      .builder()
      .message(message)
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(ConstraintViolationException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(ConstraintViolationException e) {
    log.warn("Constraint Violation", e);
    ConstraintViolation<?> violation = e.getConstraintViolations().iterator().next();
    String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
    String message = String.format("%s:%s", path, violation.getMessage());
    return BaseResponse
      .builder()
      .message(message)
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(NoHandlerFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public BaseResponse handleError(NoHandlerFoundException e) {
    log.error("404 Not Found", e);
    return BaseResponse
      .builder()
      .message(e.getMessage())
      .code(HttpStatus.NOT_FOUND.value())
      .build();
  }

  @ExceptionHandler(HttpMessageNotReadableException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(HttpMessageNotReadableException e) {
    log.error("Message Not Readable", e);
    return BaseResponse
      .builder()
      .message(e.getMessage())
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }

  @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
  public BaseResponse handleError(HttpRequestMethodNotSupportedException e) {
    log.error("Request Method Not Supported", e);
    return BaseResponse
      .builder()
      .message(e.getMessage())
      .code(HttpStatus.METHOD_NOT_ALLOWED.value())
      .build();
  }

  @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
  @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
  public BaseResponse handleError(HttpMediaTypeNotSupportedException e) {
    log.error("Media Type Not Supported", e);
    return BaseResponse
      .builder()
      .message(e.getMessage())
      .code(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value())
      .build();
  }

  @ExceptionHandler(ValidationException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public BaseResponse handleError(ValidationException e) {
    log.error("Parameter error", e);
    return BaseResponse.builder()
      .message(e.getMessage())
      .code(HttpStatus.BAD_REQUEST.value())
      .build();
  }
}
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

  public static final String TRACE = "trace";

  @Value("${reflectoring.trace:false}")
  private boolean printStackTrace;

  @Override
  @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
  protected ResponseEntity<Object> handleMethodArgumentNotValid(
      MethodArgumentNotValidException ex,
      HttpHeaders headers,
      HttpStatus status,
      WebRequest request
  ) {
      //Body omitted as it's similar to the method of same name
      // in ProductController example...  
      //.....
  }

  @ExceptionHandler(ItemNotFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public ResponseEntity<Object> handleItemNotFoundException(
      ItemNotFoundException itemNotFoundException, 
      WebRequest request
  ){
      //Body omitted as it's similar to the method of same name
      // in ProductController example...  
      //.....  
  }

  @ExceptionHandler(RuntimeException.class)
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public ResponseEntity<Object> handleAllUncaughtException(
      RuntimeException exception, 
      WebRequest request
  ){
      //Body omitted as it's similar to the method of same name
      // in ProductController example...  
      //.....
  }

  //....

  @Override
  public ResponseEntity<Object> handleExceptionInternal(
      Exception ex,
      Object body,
      HttpHeaders headers,
      HttpStatus status,
      WebRequest request) {

    return buildErrorResponse(ex,status,request);
  }

}

Complete Guide to Exception Handling in Spring Boot

Spring Boot server properties

@ExceptionHandler java documentation

@ControllerAdvice


Spring
Java, Spring, SpringBoot

Post navigation

NEXT
我的风控策略(生活篇)
PREVIOUS
编码与解码

发表回复 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

最近文章

  • 姥姥
  • 设计一个可扩展的用户模型
  • 使用 Apple 的 Keychain 保存 SSH 的 passphase
  • 解决 ABA 问题
  • 关于 macOS 上面部分 emoji 无法显示的问题
  • 这些年我技术栈的变化
  • 搬瓦工、狗云、hostodo、oracle对比测试
  • Google Drive 无法上传文件
  • 使用 socks5 代理 git ssh 协议
  • 到底要不要“润”
  • 可复用的代码
  • 关于疫情的一点点反思
  • 我的风控策略(投资篇)
  • 我的风控策略(生活篇)
  • Spring Boot是如何处理异常的
  • 编码与解码
  • 基于统计的图像目标检索
  • 有限状态机和状态模式
  • API安全设计
  • 用户密码的存储策略

近期评论

  • ǝɔɐǝԀʎzɐɹϽ发表在《可复用的代码》
  • Alex发表在《可复用的代码》
  • ǝɔɐǝԀʎzɐɹϽ发表在《到底要不要“润”》
  • ǝɔɐǝԀʎzɐɹϽ发表在《可复用的代码》
  • ǝɔɐǝԀʎzɐɹϽ发表在《我的风控策略(生活篇)》
  • ǝɔɐǝԀʎzɐɹϽ发表在《如何写出简洁优雅的代码》
  • 张志亮发表在《如何写出简洁优雅的代码》

分类

  • AI (2)
  • Java应用安全之道 (1)
    • 加密与解密 (1)
  • Odoo (2)
  • Python (1)
  • 图像处理 (1)
  • 年鉴 (1)
  • 数据库 (10)
  • 编程 (14)
    • Spring (1)
  • 读书笔记 (2)
  • 运维 (5)
  • 随笔 (10)

归档

  • 2023年1月 (2)
  • 2022年8月 (1)
  • 2022年7月 (4)
  • 2022年6月 (2)
  • 2022年5月 (2)
  • 2022年4月 (3)
  • 2021年10月 (1)
  • 2021年7月 (1)
  • 2021年5月 (1)
  • 2020年11月 (1)
  • 2020年7月 (1)
  • 2020年3月 (2)
  • 2020年2月 (1)
  • 2019年1月 (1)
  • 2018年12月 (2)
  • 2018年11月 (2)
  • 2017年4月 (1)
  • 2016年11月 (1)
  • 2016年9月 (1)
  • 2016年7月 (1)
  • 2016年5月 (3)
  • 2016年4月 (2)
  • 2016年3月 (1)
  • 2016年2月 (2)
  • 2015年12月 (1)
  • 2015年11月 (2)
  • 2015年8月 (1)
  • 2015年4月 (1)
  • 2015年3月 (1)

标签

Database devops Java MySQL PostgreSQL Python shell Spring SpringBoot Spring Security 安全 年鉴 总结 编程 随笔
© 2015-2023   Geektop.net All Rights Reserved.