Kotlin + Spring Boot请求编组

鉴于以下有效载荷:

data public class CandidateDetailDTO(val id: String, val stageName: String, val artists: Iterable<ArtistDTO>, val instruments: Iterable<InstrumentDTO>, val genres: Iterable<GenreDTO>, val discoverable: Boolean, val gender: Gender, val involvement: Involvement, val biography: String, var photoURLs: List<URL>, var birthday: Date? = null, var customGenre: String? = null) 

。 。 如图所示某些字段允许为空,其他字段则不允许。

当使用Spring Boot调用请求时,如果缺少预期字段,则返回400 – 错误请求。 这不是预料之中,我期望有关的管制员建议适用于:

 @ControllerAdvice public class SomeExceptionHandler : ResponseEntityExceptionHandler() { @ExceptionHandler(Throwable::class) @ResponseBody public fun onException(ex: Throwable): ResponseEntity<ErrorResponse> { val responseCode = ex.responseCode() val errorResponse = ErrorResponse(response = ResponseHeader(responseCode, ex.message)) return ResponseEntity(errorResponse, responseCode.httpStatus()); } } 

。 。 如果有的话,它也会返回400,还有一些关于错误的附加信息。

是否有可能配置Spring Boot行为如上所述?

默认情况下,在不干扰异常处理的情况下,Spring Boot将在响应主体中返回完整的错误信息,如JSON。 但是它不会让你在HTTP标题中设置状态码的reasonString

问题:

 @ControllerAdvice public class BadExceptionHandler: ResponseEntityExceptionHandler() { @ExceptionHandler(Throwable::class) @ResponseBody public fun onException(ex: Throwable): ResponseEntity<ErrorResponse> { val responseCode = ex.responseCode() val errorResponse = ErrorResponse(response = ResponseHeader(responseCode, ex.message)) return ResponseEntity(errorResponse, responseCode.httpStatus()); } } 

在这个例子中,你的子类是ResponseEntityExceptionHandler但是不要覆盖它的任何方法。 而是为Throwable添加另一个@ExceptionHandler ,永远不会执行。 更明确的基类注册了一个@ExceptionHandler ,它将获得胜利,因为它通过具有优先级的特定类型来注册每个异常类。

并且不小心让ResponseEntityExceptionHandler做它的默认行为(设置正文为null ),你也失去了自定义错误消息JSON响应。 ResponseEntityExceptionHandler的正确使用是对它进行子类化,然后覆盖其中一个错误方法:

  • handleBindException
  • handleMissingServletRequestParameter
  • handleServletRequestBindingException
  • handleTypeMismatch
  • handleMethodArgumentNotValid

而当重写这些方法时,你很难设置HTTP状态reasonString因为响应实体接收到一个reasonString ,它永远不会让你从一个预定义的值中改变reasonString 。 你可以用完整的错误信息返回一个主体,并让状态代码保持通用。

解决方案:

第一个选择是完全删除你的自定义错误处理程序,这是干扰默认行为,做你想要的。

第二种选择是,如果你使用ResponseEntityExceptionHandler那么你需要重写正确的方法,你可以在响应正文中返回一个带有消息的实体(请参阅: Spring Boot REST服务异常处理 )…本文涵盖了很多无论如何,你的问题。 确保你设置了一个body,而不是将null body传递给handleExceptionInternal()

第三个选项是删除ResponseEntityExceptionHandler并保持类似于您的问题的代码:

 data class ErrorResponse(val statusCode: Int, val statusMessage: String, val message: String) @ControllerAdvice public class SomeExceptionHandler { @ExceptionHandler(Throwable::class) @ResponseBody public fun onException(ex: Throwable): ResponseEntity<ErrorResponse> { val httpError = HttpStatus.BAD_REQUEST val errorResponse = ErrorResponse(httpError.value(), httpError.reasonPhrase, ex.message ?: "Bad Thing") return ResponseEntity(errorResponse, httpError); } } 

这产生了类似于:

 { "response": { "code": "VAMPServiceError", "message": "Could not read document: No suitable constructor found for type [simple type, class vampr.api.service.profile.payload.CandidateUpdateDTO]" } } 

第四个选项是创建一个不同形式的异常处理程序,它调用response.sendError()来发回JSON错误,这是Spring Boot的默认默认设置,但也允许您执行其他逻辑。 这看起来像:

 @ControllerAdvice public class UniversalExceptionHandler { @ExceptionHandler(SomeException::class) fun handleBadRequests(ex: Throwable, response: HttpServletResponse){ // .. some logic response.sendError(HttpStatus.BAD_REQUEST.value(), ex.message) } } 

这产生:

 { "timestamp": 1452730838233, "status": 400, "error": "Bad Request", "exception": "org.springframework.web.bind.MissingServletRequestParameterException", "message": "Required String parameter 'name' is not present", "path": "/greeting" } 

也可以看看:

  • Spring Boot Error Repsonses(jayway博客)
  • SO: Spring Boot自定义http错误响应?
  • SO: Spring Boot REST服务异常处理
  • SO: spring-boot-starter-web中默认的JSON错误响应来自哪里,以及如何调整?
  • SO: 修改Spring Boot Rest Controller的默认JSON错误响应
  • 从Spring Boot处理文档时出错
  • ResponseEntityExceptionHandler JavaDoc(SpringMVC的一部分)
  • 改进你的Spring REST API第三部分(jayway博客)
  • 处理Spring MVC异常 – 什么异常类是什么
  • Spring HATEOAS允许在Spring教程中使用VndError编码和更多的信息和示例编码更多的错误informatino。
Interesting Posts