The exception handling package Global springboot

The exception handling package Global springboot

Brief introduction

System abnormalities often the case in the project, for example NullPointerException, and so on. If untreated by default, springbootit will respond to a default error message, so the user experience is not a friendly system-level error, the user can not perceive, even as 500mistakes, can prompt a similar user 服务器开小差-friendly tips.

In the micro-services, each service will have exceptions, almost all of the default service configuration consistent exception handling, resulting in a lot of repetition coding, we will repeat these default exception handler can extract a common starterpackage, you can rely on each service, customized Exception handling in the various modules in development.

Configuration

unified-dispose-springboot-starter

This module contains the global error processing and return packaging functions, below.

Full directory structure is as follows:

├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── purgetiem
│   │   │           └── starter
│   │   │               └── dispose
│   │   │                   ├── GlobalDefaultConfiguration.java
│   │   │                   ├── GlobalDefaultProperties.java
│   │   │                   ├── Interceptors.java
│   │   │                   ├── Result.java
│   │   │                   ├── advice
│   │   │                   │   └── CommonResponseDataAdvice.java
│   │   │                   ├── annotation
│   │   │                   │   ├── EnableGlobalDispose.java
│   │   │                   │   └── IgnorReponseAdvice.java
│   │   │                   └── exception
│   │   │                       ├── GlobalDefaultExceptionHandler.java
│   │   │                       ├── category
│   │   │                       │   └── BusinessException.java
│   │   │                       └── error
│   │   │                           ├── CommonErrorCode.java
│   │   │                           └── details
│   │   │                               └── BusinessErrorCode.java
│   │   └── resources
│   │       ├── META-INF
│   │       │   └── spring.factories
│   │       └── dispose.properties
│   └── test
│       └── java

Exception Handling

@RestControllerAdviceOr @ControllerAdviceas springexception handling annotations.

Let's create a GlobalDefaultExceptionHandlerglobal exception handler class:

@RestControllerAdvice
public class GlobalDefaultExceptionHandler {

  private static final Logger log = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class);

  /**
   * NoHandlerFoundException 404 异常处理
   */
  @ExceptionHandler(value = NoHandlerFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public Result handlerNoHandlerFoundException(NoHandlerFoundException exception) {
    outPutErrorWarn(NoHandlerFoundException.class, CommonErrorCode.NOT_FOUND, exception);
    return Result.ofFail(CommonErrorCode.NOT_FOUND);
  }

  /**
   * HttpRequestMethodNotSupportedException 405 异常处理
   */
  @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  public Result handlerHttpRequestMethodNotSupportedException(
      HttpRequestMethodNotSupportedException exception) {
    outPutErrorWarn(HttpRequestMethodNotSupportedException.class,
        CommonErrorCode.METHOD_NOT_ALLOWED, exception);
    return Result.ofFail(CommonErrorCode.METHOD_NOT_ALLOWED);
  }

  /**
   * HttpMediaTypeNotSupportedException 415 异常处理
   */
  @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
  public Result handlerHttpMediaTypeNotSupportedException(
      HttpMediaTypeNotSupportedException exception) {
    outPutErrorWarn(HttpMediaTypeNotSupportedException.class,
        CommonErrorCode.UNSUPPORTED_MEDIA_TYPE, exception);
    return Result.ofFail(CommonErrorCode.UNSUPPORTED_MEDIA_TYPE);
  }

  /**
   * Exception 类捕获 500 异常处理
   */
  @ExceptionHandler(value = Exception.class)
  public Result handlerException(Exception e) {
    return ifDepthExceptionType(e);
  }

  /**
   * 二次深度检查错误类型
   */
  private Result ifDepthExceptionType(Throwable throwable) {
    Throwable cause = throwable.getCause();
    if (cause instanceof ClientException) {
      return handlerClientException((ClientException) cause);
    }
    if (cause instanceof FeignException) {
      return handlerFeignException((FeignException) cause);
    }
    outPutError(Exception.class, CommonErrorCode.EXCEPTION, throwable);
    return Result.ofFail(CommonErrorCode.EXCEPTION);
  }

  /**
   * FeignException 类捕获
   */
  @ExceptionHandler(value = FeignException.class)
  public Result handlerFeignException(FeignException e) {
    outPutError(FeignException.class, CommonErrorCode.RPC_ERROR, e);
    return Result.ofFail(CommonErrorCode.RPC_ERROR);
  }

  /**
   * ClientException 类捕获
   */
  @ExceptionHandler(value = ClientException.class)
  public Result handlerClientException(ClientException e) {
    outPutError(ClientException.class, CommonErrorCode.RPC_ERROR, e);
    return Result.ofFail(CommonErrorCode.RPC_ERROR);
  }

  /**
   * BusinessException 类捕获
   */
  @ExceptionHandler(value = BusinessException.class)
  public Result handlerBusinessException(BusinessException e) {
    outPutError(BusinessException.class, CommonErrorCode.BUSINESS_ERROR, e);
    return Result.ofFail(e.getCode(), e.getMessage());
  }

  /**
   * HttpMessageNotReadableException 参数错误异常
   */
  @ExceptionHandler(HttpMessageNotReadableException.class)
  public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
    outPutError(HttpMessageNotReadableException.class, CommonErrorCode.PARAM_ERROR, e);
    String msg = String.format("%s : 错误详情( %s )", CommonErrorCode.PARAM_ERROR.getMessage(),
        e.getRootCause().getMessage());
    return Result.ofFail(CommonErrorCode.PARAM_ERROR.getCode(), msg);
  }

  /**
   * BindException 参数错误异常
   */
  @ExceptionHandler(BindException.class)
  public Result handleMethodArgumentNotValidException(BindException e) {
    outPutError(BindException.class, CommonErrorCode.PARAM_ERROR, e);
    BindingResult bindingResult = e.getBindingResult();
    return getBindResultDTO(bindingResult);
  }

  private Result getBindResultDTO(BindingResult bindingResult) {
    List<FieldError> fieldErrors = bindingResult.getFieldErrors();
    if (log.isDebugEnabled()) {
      for (FieldError error : fieldErrors) {
        log.error("{} -> {}", error.getDefaultMessage(), error.getDefaultMessage());
      }
    }

    if (fieldErrors.isEmpty()) {
      log.error("validExceptionHandler error fieldErrors is empty");
      Result.ofFail(CommonErrorCode.BUSINESS_ERROR.getCode(), "");
    }

    return Result
        .ofFail(CommonErrorCode.PARAM_ERROR.getCode(), fieldErrors.get(0).getDefaultMessage());
  }

  public void outPutError(Class errorType, Enum secondaryErrorType, Throwable throwable) {
    log.error("[{}] {}: {}", errorType.getSimpleName(), secondaryErrorType, throwable.getMessage(),
        throwable);
  }

  public void outPutErrorWarn(Class errorType, Enum secondaryErrorType, Throwable throwable) {
    log.warn("[{}] {}: {}", errorType.getSimpleName(), secondaryErrorType, throwable.getMessage());
  }

}

The general content of the project dealt with some common exceptions Exception, BindExceptionparameters disorders.

Here the default 404, 405, 415and other default httpstatus code is also rewritten.

Override this default status code needs to be configured throw-exception-if-no-handler-foundas well add-mappings.

# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)
spring.mvc.throw-exception-if-no-handler-found=true
# 是否开启默认的资源处理,默认为true
spring.resources.add-mappings=false

ps: Note that both configured static resources will be ignored.

Please test productionWebMvcAutoConfiguration#addResourceHandlers

ExceptionTo prevent some exceptions to the unprotected, the user returns to the default 服务器开小差,请稍后再试and other tips.

Specific abnormalities default small to large to match.

If you throw BindException, the custom there BindExceptionwill go to this processor in the process. Will not go to match its parent class, please refer to java-异常体系.

img

Other known anomalies can own @ExceptionHandlercapture process annotations.

General abnormal Enumeration

In order to avoid outliers bad maintenance, we used CommonErrorCodeto enumerate the common abnormalities tips to maintain them.

@Getter
public enum CommonErrorCode {

  /**
   * 404 Web 服务器找不到您所请求的文件或脚本。请检查URL 以确保路径正确。
   */
  NOT_FOUND("CLOUD-404",
      String.format("哎呀,无法找到这个资源啦(%s)", HttpStatus.NOT_FOUND.getReasonPhrase())),

  /**
   * 405 对于请求所标识的资源,不允许使用请求行中所指定的方法。请确保为所请求的资源设置了正确的 MIME 类型。
   */
  METHOD_NOT_ALLOWED("CLOUD-405",
      String.format("请换个姿势操作试试(%s)", HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase())),

  /**
   * 415 Unsupported Media Type
   */
  UNSUPPORTED_MEDIA_TYPE("CLOUD-415",
      String.format("呀,不支持该媒体类型(%s)", HttpStatus.UNSUPPORTED_MEDIA_TYPE.getReasonPhrase())),

  /**
   * 系统异常 500 服务器的内部错误
   */
  EXCEPTION("CLOUD-500", "服务器开小差,请稍后再试"),

  /**
   * 系统限流
   */
  TRAFFIC_LIMITING("CLOUD-429", "哎呀,网络拥挤请稍后再试试"),

  /**
   * 服务调用异常
   */
  API_GATEWAY_ERROR("API-9999", "网络繁忙,请稍后再试"),

  /**
   * 参数错误
   */
  PARAM_ERROR("CLOUD-100", "参数错误"),

  /**
   * 业务异常
   */
  BUSINESS_ERROR("CLOUD-400", "业务异常"),

  /**
   * rpc调用异常
   */
  RPC_ERROR("RPC-510", "呀,网络出问题啦!");

  private String code;

  private String message;

  CommonErrorCode(String code, String message) {
    this.code = code;
    this.message = message;
  }
}

In fact, starterthe package is not recommended for use @Getterand other lomboknotes, to prevent others from unused lombokdependent on the project problems.

General business exceptions

These two classes can perform basic normal use exceptions intercepted, but for the convenience of business, we create a general generic business exception.

BusinessExceptionInheritance RuntimeExceptioncan be.

@Getter
public class BusinessException extends RuntimeException {

  private String code;
  private boolean isShowMsg = true;

  /**
   * 使用枚举传参
   *
   * @param errorCode 异常枚举
   */
  public BusinessException(BusinessErrorCode errorCode) {
    super(errorCode.getMessage());
    this.code = errorCode.getCode();
  }

  /**
   * 使用自定义消息
   *
   * @param code 值
   * @param msg 详情
   */
  public BusinessException(String code, String msg) {
    super(msg);
    this.code = code;
  }

}

Will BusinessExceptionjoin GlobalDefaultExceptionHandlerglobal exception interception.

/**
 * BusinessException 类捕获
 */
@ExceptionHandler(value = BusinessException.class)
public Result handlerBusinessException(BusinessException e) {
  outPutError(BusinessException.class, CommonErrorCode.BUSINESS_ERROR, e);
  return Result.ofFail(e.getCode(), e.getMessage());
}

Program initiative thrown by the following methods:

throw new BusinessException(BusinessErrorCode.BUSINESS_ERROR);
// 或者
throw new BusinessException("CLOUD800","没有多余的库存");

Usually do not recommend direct universal BusinessException throw an exception, exception handling class should be added and the corresponding enumeration areas corresponding to the type of error in the corresponding module.

If a Member module:
create UserExceptionan exception class, UserErrorCodeenumeration, and UserExceptionHandlerunified interception class.

UserException:

@Data
public class UserException extends RuntimeException {

  private String code;
  private boolean isShowMsg = true;

  /**
   * 使用枚举传参
   *
   * @param errorCode 异常枚举
   */
  public UserException(UserErrorCode errorCode) {
    super(errorCode.getMessage());
    this.setCode(errorCode.getCode());
  }

}

User Error Code:

@Getter
public enum UserErrorCode {
    /**
     * 权限异常
     */
    NOT_PERMISSIONS("CLOUD401","您没有操作权限"),
    ;

    private String code;

    private String message;

    CommonErrorCode(String code, String message) {
        this.code = code;
        this.message = message;
    }
}

UserExceptionHandler:

@Slf4j
@RestControllerAdvice
public class UserExceptionHandler {

  /**
   * UserException 类捕获
   */
  @ExceptionHandler(value = UserException.class)
  public Result handler(UserException e) {
    log.error(e.getMessage(), e);
    return Result.ofFail(e.getCode(), e.getMessage());
  }

}

Finally, use the following services:

// 判断是否有权限抛出异常
throw new UserException(UserErrorCode.NOT_PERMISSIONS);

Join spring container

Finally, GlobalDefaultExceptionHandlerin a beanmanner injection springcontainer.

@Configuration
@EnableConfigurationProperties(GlobalDefaultProperties.class)
@PropertySource(value = "classpath:dispose.properties", encoding = "UTF-8")
public class GlobalDefaultConfiguration {

  @Bean
  public GlobalDefaultExceptionHandler globalDefaultExceptionHandler() {
    return new GlobalDefaultExceptionHandler();
  }

  @Bean
  public CommonResponseDataAdvice commonResponseDataAdvice(GlobalDefaultProperties globalDefaultProperties){
    return new CommonResponseDataAdvice(globalDefaultProperties);
  }

}

Will be GlobalDefaultConfigurationin resources/META-INF/spring.factoriesload the next file.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.purgetime.starter.dispose.GlobalDefaultConfiguration

But this time we use the annotation mode is turned on. After the other project dependencies, you need to add @EnableGlobalDisposeonly a global interception feature can be turned on.

Just created spring.factoriescommented, create EnableGlobalDisposenotes.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(GlobalDefaultConfiguration.class)
public @interface EnableGlobalDispose {

}

Use @Importthe GlobalDefaultConfigurationimport can be.

use

Add dependent

<dependency>
  <groupId>io.deepblueai</groupId>
  <artifactId>unified-dispose-deepblueai-starter</artifactId>
  <version>0.1.0.RELEASE</version>
</dependency>

Start open class @EnableGlobalDisposenotes can be.

to sum up

Project in a lot of duplicate code, we can go through some simplified way to achieve a certain objective of reducing the amount of development.

Sample Code Address: Unified-Dispose-springboot

On GitHub:
Purgeyao welcome attention

Guess you like

Origin www.cnblogs.com/Purgeyao/p/11566469.html