给定以下有效负载:
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)
。 。如图所示,某些字段允许使用null,而其他字段则不允许。
使用Spring Boot调用请求时,如果缺少期望的字段,则返回400-Bad Request。这不是很期望,我希望适用相关的控制器建议:
@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
子类化,但是不要覆盖其任何方法。相反,您为永远不会执行的@ExceptionHandler
添加了另一个Throwable
。更明确的基类将注册一个@ExceptionHandler
,因为它会通过具有优先级的特定类型注册每个异常类,因此它将获胜。而且,由于不小心让
ResponseEntityExceptionHandler
执行其默认行为(将主体设置为null
),您还丢失了自定义错误消息JSON响应。 ResponseEntityExceptionHandler
的正确用法是对其进行子类化,然后覆盖其中一种错误方法:并且,当覆盖这些方法时,您将很难设置HTTP状态
reasonString
,因为响应实体会收到HttpStatusCode
,它永远不允许您从预定义值之一更改reasonString
。您可以改为返回带有完整错误信息的正文,并使状态代码保持通用。解决方案:
第一个选项是完全删除您的自定义错误处理程序,它干扰了已经达到您想要的默认行为。
第二个选项,如果您使用
ResponseEntityExceptionHandler
,那么您需要重写正确的方法,以便您可以在响应的正文中返回带有消息的实体(请参阅:Spring Boot REST service exception handling)...无论如何,本文涵盖了很多问题。确保设置一个主体,而不是将null
主体传递给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"
}
也可以看看:
关于java - Kotlin + Spring Boot请求编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34762122/