java - Kotlin + Spring Boot请求编码

标签 java spring spring-boot jackson kotlin

给定以下有效负载:

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的正确用法是对其进行子类化,然后覆盖其中一种错误方法:
  • handleBindException
  • handleMissingServletRequestParameter
  • handleServletRequestBindingException
  • handleTypeMismatch
  • handleMethodArgumentNotValid
  • ...

  • 并且,当覆盖这些方法时,您将很难设置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"
    }
    

    也可以看看:
  • Spring Boot Error Repsonses (jayway blog)
  • SO:Spring Boot customize http error response?
  • SO:Spring Boot REST service exception handling
  • SO:Where does the default JSON errors response in spring-boot-starter-web comes from and how to adjust it?
  • SO:Modify default JSON error response from Spring Boot Rest Controller
  • Error handling docs from Spring Boot
  • ResponseEntityExceptionHandler JavaDoc (part of SpringMVC)
  • Improve your Spring REST API part III (jayway blog)
  • Handling Spring MVC Exceptions-什么是
  • 的异常类
  • Spring HATEOAS允许在Spring教程中使用VndError encodingmore info and sample编码更多错误信息。
  • 关于java - Kotlin + Spring Boot请求编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34762122/

    相关文章:

    javascript - location.href 的正确语法是什么

    java - 如何在 Web 服务的 Java HttpsServer 实现中获取客户端证书?

    java - 如何使用 target/...jar 执行 scala 对象?

    java - 将 WINDOWS-MY keystore 与 JDK 1.4 结合使用

    java - Spring Security 4.2 不工作

    java - Hibernate 中 NamedParameterJDBCtemplate rowmapper 的替代方案

    java - Spring Boot 独立应用程序在生产中部署

    java - Comparator compare() 如何在内部为下面实现的方法工作?

    java - 绑定(bind)Spring的HibernateDaoSupport时如何设置数据源

    java - Spring Data/Hibernate @ManyToOne 在不应该的情况下自动级联