弹簧靴返回200并带有"OK"附加到正常正文响应的错误属性


  • Spring boot starter version: 2.3.4.RELEASE
  • SPRINGWEB 5.2.8版本

我的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));
}

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

然而,需要更多调查的是,上下文和过滤器中的失败(与spring框架完全无关)如何仍然允许过滤器链执行继续并调用控制器并返回200,然后附加一个看似ErrorAttribute的响应和200"OK"消息。

最新更新