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。