美文网首页
微服务架构统一异常处理规范

微服务架构统一异常处理规范

作者: 花枫天o | 来源:发表于2018-04-26 15:28 被阅读0次

1. 统一使用ResponseEntity类 : 用于统一响应格式

  • 使用org.springframework.http.ResponseEntity作为统一响应格式

  • 示例:

    @GetMapping("/as/{id}")
    @Timed
    public ResponseEntity<A> getA(@PathVariable Long id) {
        log.debug("REST request to get A : {}", id);
        A a = aService.findOne(id);
        return ResponseUtil.wrapOrNotFound(Optional.ofNullable(a));
    }
    

2.统一的Error类 : 用于统一异常格式

  • 统一使用org.zalando.problem.ThrowableProblem的子类作为异常格式
image.png
  • 统一使用httpStatus作为错误编码

    public enum Status implements StatusType {
       CONTINUE(100, "Continue"),
       SWITCHING_PROTOCOLS(101, "Switching Protocols"),
       PROCESSING(102, "Processing"),
       CHECKPOINT(103, "Checkpoint"),
       OK(200, "OK"),
       CREATED(201, "Created"),
       ACCEPTED(202, "Accepted"),
       NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
       NO_CONTENT(204, "No Content"),
       RESET_CONTENT(205, "Reset Content"),
       PARTIAL_CONTENT(206, "Partial Content"),
       MULTI_STATUS(207, "Multi-Status"),
       ALREADY_REPORTED(208, "Already Reported"),
       IM_USED(226, "IM Used"),
       MULTIPLE_CHOICES(300, "Multiple Choices"),
       MOVED_PERMANENTLY(301, "Moved Permanently"),
       FOUND(302, "Found"),
       SEE_OTHER(303, "See Other"),
       NOT_MODIFIED(304, "Not Modified"),
       USE_PROXY(305, "Use Proxy"),
       TEMPORARY_REDIRECT(307, "Temporary Redirect"),
       PERMANENT_REDIRECT(308, "Permanent Redirect"),
       BAD_REQUEST(400, "Bad Request"),
       UNAUTHORIZED(401, "Unauthorized"),
       PAYMENT_REQUIRED(402, "Payment Required"),
       FORBIDDEN(403, "Forbidden"),
       NOT_FOUND(404, "Not Found"),
       METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
       NOT_ACCEPTABLE(406, "Not Acceptable"),
       PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
       REQUEST_TIMEOUT(408, "Request Timeout"),
       CONFLICT(409, "Conflict"),
       GONE(410, "Gone"),
       LENGTH_REQUIRED(411, "Length Required"),
       PRECONDITION_FAILED(412, "Precondition Failed"),
       REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
       REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
       UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
       REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
       EXPECTATION_FAILED(417, "Expectation Failed"),
       I_AM_A_TEAPOT(418, "I'm a teapot"),
       UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
       LOCKED(423, "Locked"),
       FAILED_DEPENDENCY(424, "Failed Dependency"),
       UPGRADE_REQUIRED(426, "Upgrade Required"),
       PRECONDITION_REQUIRED(428, "Precondition Required"),
       TOO_MANY_REQUESTS(429, "Too Many Requests"),
       REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"),
       INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
       NOT_IMPLEMENTED(501, "Not Implemented"),
       BAD_GATEWAY(502, "Bad Gateway"),
       SERVICE_UNAVAILABLE(503, "Service Unavailable"),
       GATEWAY_TIMEOUT(504, "Gateway Timeout"),
       HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"),
       VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"),
       INSUFFICIENT_STORAGE(507, "Insufficient Storage"),
       LOOP_DETECTED(508, "Loop Detected"),
       BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
       NOT_EXTENDED(510, "Not Extended"),
       NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
    
       private final int code;
       private final String reason;
    
       private Status(int statusCode, String reasonPhrase) {
           this.code = statusCode;
           this.reason = reasonPhrase;
       }
    
       public int getStatusCode() {
           return this.code;
      }
    
       public String getReasonPhrase() {
           return this.reason;
       }
     }
    

3.自定义异常 : 区分不同场景的异常

  • 请求异常

    public class BadRequestAlertException extends AbstractThrowableProblem{
    
       private final String entityName;
    
       private final String errorKey;
    
       public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
           this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
       }
    
       public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
           super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
           this.entityName = entityName;
           this.errorKey = errorKey;
       }
    
       public String getEntityName() {
           return entityName;
       }
    
       public String getErrorKey() {
           return errorKey;
       }
    
       private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
           Map<String, Object> parameters = new HashMap<>();
           parameters.put("message", "error." + errorKey);
           parameters.put("params", entityName);
           return parameters;
       }
    

    }

  • 参数异常

    public class CustomParameterizedException extends AbstractThrowableProblem {
    
       private static final long serialVersionUID = 1L;
    
       private static final String PARAM = "param";
    
       public CustomParameterizedException(String message, String... params) {
           this(message, toParamMap(params));
       }
    
       public CustomParameterizedException(String message, Map<String, Object> paramMap) {
           super(ErrorConstants.PARAMETERIZED_TYPE, "Parameterized Exception", BAD_REQUEST, null, null, null, toProblemParameters(message, paramMap));
       }
    
       public static Map<String, Object> toParamMap(String... params) {
           Map<String, Object> paramMap = new HashMap<>();
           if (params != null && params.length > 0) {
               for (int i = 0; i < params.length; i++) {
                   paramMap.put(PARAM + i, params[i]);
               }
           }
           return paramMap;
       }
    
       public static Map<String, Object> toProblemParameters(String message, Map<String, Object> paramMap) {
           Map<String, Object> parameters = new HashMap<>();
           parameters.put("message", message);
           parameters.put("params", paramMap);
           return parameters;
       }
    

    }

  • 并发异常
    org.springframework.dao.ConcurrencyFailureException

  • 其他异常
    按需定义,继承org.zalando.problem.ThrowableProblem即可

4.实现ExceptionHandler : 用于拦截处理异常

@ControllerAdvice
public class ExceptionTranslator implements ProblemHandling {

  /**
   * Post-process Problem payload to add the message key for front-end if needed
   */
  @Override
  public ResponseEntity<Problem> process(@Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
      if (entity == null || entity.getBody() == null) {
          return entity;
      }
      Problem problem = entity.getBody();
      if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
          return entity;
      }
      ProblemBuilder builder = Problem.builder()
        .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
        .withStatus(problem.getStatus())
        .withTitle(problem.getTitle())
        .with("path",   request.getNativeRequest(HttpServletRequest.class).getRequestURI());

      if (problem instanceof ConstraintViolationProblem) {
          builder
            .with("violations", ((ConstraintViolationProblem) problem).getViolations())
            .with("message", ErrorConstants.ERR_VALIDATION);
        return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
      } else {
          builder
            .withCause(((DefaultProblem) problem).getCause())
            .withDetail(problem.getDetail())
            .withInstance(problem.getInstance());
        problem.getParameters().forEach(builder::with);
        if (!problem.getParameters().containsKey("message") && problem.getStatus() != null) {
            builder.with("message", "error.http." + problem.getStatus().getStatusCode());
        }
        return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
    }
}

  @Override
  public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
      BindingResult result = ex.getBindingResult();
      List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
        .map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode()))
        .collect(Collectors.toList());

      Problem problem = Problem.builder()
        .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
        .withTitle("Method argument not valid")
        .withStatus(defaultConstraintViolationStatus())
        .with("message", ErrorConstants.ERR_VALIDATION)
        .with("fieldErrors", fieldErrors)
        .build();
      return create(ex, problem, request);
  }

  @ExceptionHandler(BadRequestAlertException.class)
  public ResponseEntity<Problem> handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
      return create(ex, request, HeaderUtil.createFailureAlert(ex.getEntityName(), ex.getErrorKey(), ex.getMessage()));
  }

  @ExceptionHandler(ConcurrencyFailureException.class)
  public ResponseEntity<Problem> handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) {
      Problem problem = Problem.builder()
        .withStatus(Status.CONFLICT)
        .with("message", ErrorConstants.ERR_CONCURRENCY_FAILURE)
        .build();
      return create(ex, problem, request);
  }
}

5.统一业务异常抛出

异常统一在service或者controller里面抛出,抛出异常类型为BadRequestAlertException

  • 例子
@PostMapping("/as")
@Timed
public ResponseEntity<A> createA(@Valid @RequestBody A a) throws URISyntaxException {
    log.debug("REST request to save A : {}", a);
    if (a.getId() != null) {
        throw new BadRequestAlertException("A new a cannot already have an ID", ENTITY_NAME, "idexists");
    }
    A result = aService.save(a);
    return ResponseEntity.created(new URI("/api/as/" + result.getId()))
        .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
        .body(result);
}

相关文章

  • 微服务架构统一异常处理规范

    1. 统一使用ResponseEntity类 : 用于统一响应格式 使用org.springframework.h...

  • 统一异常处理导致ResponseBodyAdvice失效

    背景 微服务架构下准备将一些基础功能抽出到公共Jar包中,包括统一异常处理、JwtToken校验、统一请求响应处理...

  • PM篇

    PM 技术篇1.开发规范命名规范,异常处理规范,日志规范,统一框架,代码commit规范,代码评审规范,统一API...

  • java web编码规范

    架构师 : 定义公司系统脚手架 (1)日志统一处理(aop) (2) 异常统一处理(aop) (3) 异常定义...

  • Restful接口统一异常处理

    1.背景 目前微服务开发越来越多,接口之间的交互也越多。考虑到一些异常情况,接口需要对异常进行统一处理,规范异常处...

  • 「Java架构师」基础部分所能学到的知识点,还在为你的技术犹豫吗

    架构师基础 工程构建管理:Maven 、GIT、SVN; Java开发规范:编程规约、异常处理、日志处理、单元测试...

  • 项目开发

    Retrofit封装 RxJava 与 Retrofit 结合的最佳实践 flatmap统一处理异常 Rx处理服务...

  • spring/springmvc 全局异常处理

    1.在项目中为什么要统一异常处理 当异常返回到前端页面的时候可以统一处理,避免前端无法处理异常 不做统一异常处理,...

  • Java架构之路- 统一异常处理

    在web应用中请求处理时,出现异常是非常常见的。所以当应用出现各类异常时,进行异常的捕获和处理非常重要的,本文重点...

  • 统一异常处理

    一、什么是统一异常处理 1、制造异常 2、什么是统一异常处理我们想让异常结果也显示为统一的返回结果对象,并且统一处...

网友评论

      本文标题:微服务架构统一异常处理规范

      本文链接:https://www.haomeiwen.com/subject/bwwplftx.html