首页 > 解决方案 > spring boot 返回 200,并将“OK”ErrorAttributes 附加到正常的身体响应

问题描述

我的 get 请求开始响应附加到响应正文的无关的“OK”ErrorAttributes,这破坏了上游客户端上的所有 json 反序列化

HTTP/1.1 200 
Date: Tue, 26 Jan 2021 07:44:00 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Trace-Id: 55418518fcaa412b
X-Span-Id: 55418518fcaa412b
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: HEAD, GET, OPTIONS, POST, PUT, PATCH, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: *
Strict-Transport-Security: max-age=63072000; includeSubdomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff

{
    "id": "ab160a6c-fe00-4eff-a63d-7c10e1c0607c",
    "nickName": "Test User",
    "emailAddress": "test.user@test-domain.com",
    "phoneNumber": null,
    "firstName": "Test",
    "lastName": "User'",
    "location": {
        "id": "a77381f2-5c89-4134-bdad-229ad5e7c9e2",
        "latitude": null,
        "longitude": null,
        "country": "DE",
        "stateOrRegion": null,
        "city": null
    },,
    "avatarUrl": null,
    "role": "DEFAULT",
    "invitationLink": "https://www.test-domain.com/app/app-invite/b666ee33-2046-4ea3-af80-f310756a2eac",
    "invitee": null
}{
    "timestamp": "2021-01-26T07:44:00.826+00:00",
    "status": 200,
    "error": "OK",
    "message": "",
    "path": "/api/users/ab160a6c-fe00-4eff-a63d-7c10e1c0607c"
}

我被困了2天,想知道是否有人知道这个问题以及如何解决它。

更新 我有异常处理程序,返回不同的 json 模型:


  @ResponseBody
  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ErrorModel handleValidationExceptions(@Nonnull final MethodArgumentNotValidException ex) {
    log.error("Error executing request:", ex);

    final List<PropertyErrorModel> propertyErrors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(this::buildProperty)
            .collect(toList());
    final List<String> globalMessages = ex.getBindingResult().getGlobalErrors()
            .stream()
            .map(ObjectError::getDefaultMessage)
            .filter(Objects::nonNull)
            .collect(toList());
    return getErrorModelBuilder()
            .globalErrors(globalMessages)
            .propertyErrors(propertyErrors)
            .build();
  }

  @ResponseBody
  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(ConstraintViolationException.class)
  public ErrorModel handleConstraintViolation(@Nonnull final ConstraintViolationException ex) {
    log.error("Error executing request:", ex);

    final List<PropertyErrorModel> propertyErrors = ex.getConstraintViolations()
            .stream()
            .map((viol) -> {
              return PropertyErrorModel.builder()
                      .property(viol.getPropertyPath().toString())
                      .rejectedValue(viol.getInvalidValue())
                      .message(viol.getMessage())
                      .build();
            })
            .collect(toList());
    final List<String> globalMessages = List.of(ex.getMessage());
    return getErrorModelBuilder()
            .code("validation.invalid.property")
            .globalErrors(globalMessages)
            .propertyErrors(propertyErrors)
            .build();
  }

  @ResponseBody
  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(ValidationException.class)
  public ErrorModel handleValidationException(@Nonnull final ValidationException ex) {
    log.error("Error executing request:", ex);

    final ErrorModel error = ex.getError();
    if (error != null) {
      return getErrorModelBuilder()
              .code(firstNonNull(error.getCode(), "validation.failed"))
              .globalErrors(error.getGlobalErrors())
              .propertyErrors(error.getPropertyErrors())
              .build();
    }

    final List<PropertyErrorModel> propertyErrors = emptyIfNull(ex.getPropertyErrors());
    final List<String> globalMessages = List.of(ex.getMessage());
    return getErrorModelBuilder()
            .code("validation.failed")
            .globalErrors(globalMessages)
            .propertyErrors(propertyErrors)
            .build();
  }

  @SneakyThrows
  @ResponseBody
  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(AlreadyExistsException.class)
  public ErrorModel handleAlreadyExistsException(@Nonnull final AlreadyExistsException ex) {
    log.error("Error executing request:", ex);

    final Object domain = ex.getDomain();
    if (domain == null) {
      final List<String> globalMessages = List.of(ex.getMessage());
      return getErrorModelBuilder()
              .code("validation.failed")
              .globalErrors(globalMessages)
              .build();
    }

    final String domainJson = objectMapper.writeValueAsString(domain);
    final List<String> globalMessages = List.of(ex.getMessage(), domainJson);
    return getErrorModelBuilder()
            .code("validation.failed")
            .globalErrors(globalMessages)
            .build();
  }

  @ResponseBody
  @ResponseStatus(HttpStatus.CONFLICT)
  @ExceptionHandler(UnsupportedFilterPropertyException.class)
  public ErrorModel handleUnsupportedFilterPropertyException(@Nonnull final UnsupportedFilterPropertyException ex) {
    log.error("Error executing request:", ex);

    if (ex.getPropertyError() != null) {
      return getErrorModelBuilder()
              .code("validation.failed")
              .propertyErrors(ex.getPropertyError())
              .build();
    }

    final List<String> globalMessages = List.of(ex.getMessage());
    return getErrorModelBuilder()
            .code("validation.failed")
            .globalErrors(globalMessages)
            .build();
  }

  @ResponseBody
  @ResponseStatus(HttpStatus.NOT_FOUND)
  @ExceptionHandler(NotFoundException.class)
  public ErrorModel handleNotFoundException(@Nonnull final NotFoundException ex) {
    log.warn("Error executing request:", ex);

    final List<String> globalMessages = List.of(ex.getMessage());
    return getErrorModelBuilder()
            .code("resource.notfound")
            .globalErrors(globalMessages)
            .build();
  }

  @ResponseBody
  @ExceptionHandler(Throwable.class)
  public ResponseEntity<ErrorModel> fallbackErrorHandler(@Nonnull final Throwable ex) {
    log.error("Error executing request:", ex);

    final Class<?> errorClass = ex.getClass();
    return Optional.ofNullable(errorCodedExceptions.get(errorClass))
            .map((errorCode) -> getCodedError(ex, errorCode))
            .orElseGet(() -> getDefaultInternalServerError(ex));
  }

标签: javaspringspring-bootspring-mvc

解决方案


过滤器中旧的脏上下文导致的问题。当抛出异常时,过滤器执行未能清理数据上下文,但不幸的是,在跟踪启动之前这太早失败了。错误日志从未出现在具有适当 trace.id 的 kibana 日志中。(我们的跟踪过滤器的优先级不够高)

然而,需要更多调查的是,与 Spring 框架完全无关的过滤器中的上下文和故障如何仍然允许过滤器链执行继续并调用控制器并返回200,然后附加一个看似 ErrorAttribute 响应,并带有200“好的”消息。


推荐阅读