【问题标题】:Spring-Boot removes message from ErrorAttributes in responseSpring-Boot 从 ErrorAttributes 中删除消息作为响应
【发布时间】:2021-01-28 17:15:25
【问题描述】:

当从 spring-boot 控制器抛出异常时,服务器响应中的消息为空 - 但前提是我不在本地运行。最后一部分是最让我困惑的部分。我的意思是,能够让 spring-boot 从错误响应中删除部分是非常有意义的。例如,就像堆栈跟踪一样,除了调试时,没有人愿意将其发送出去。

但是当我在本地运行应用程序时,我得到了完整的错误响应、消息、堆栈跟踪和所有内容(即使不是在调试模式下运行,我首先怀疑这可能是造成这种情况的原因)。一个典型的错误可能如下所示:

{
    "timestamp": "2020-10-14T09:46:35.784+00:00",
    "status": 400,
    "error": "Bad Request",
    "trace": "webcam.yellow.service.controller.error.BadRequestException: New password must be different from old password! at /*REDACTED*/",
    "message": "New password must be different from old password!",
    "path": "/users/9"
}

但是当我在部署的服务器上产生同样的错误时,我得到的只是:

{
    "timestamp": "2020-10-14T09:29:57.720+00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "",
    "path": "/users/9"
}

我完全不介意堆栈跟踪被删除(实际上我希望它被删除),但我真的很想收到那个错误消息。

我的一个想法是它可能与跨域访问有关,但是当我通过 swagger 而不是我们的前端产生错误时,我得到了相同的行为,并且 swagger 是同源的。

我完全希望这种行为可以在 spring-boot 中进行配置,这会很方便。麻烦的是,我没有配置它。我将正在运行的服务器的配置属性与我的本地配置属性进行了比较,但我没有看到任何可能对此负责的属性。如果我用谷歌搜索,我也找不到任何东西。根据我找到的所有教程,这应该可以正常工作。它确实是这样,除了不在运行的服务器上。

有谁知道 spring-boot 中是什么导致了这种行为以及如何配置它?顺便使用spring-boot 2.3.3。

其他信息:

经过一番折腾,我设法在本地重现了该问题。如果我构建应用程序,我会得到缩短的错误响应,然后直接使用 java -jar 从命令行运行它。运行 gradle bootRun 会导致服务器返回完整的错误消息。

我尝试通过 ControllerAdvice 返回我自己的错误响应:

@ControllerAdvice
class BadRequestHandler : ResponseEntityExceptionHandler() {

    @ExceptionHandler(value = [BadRequestException::class])
    protected fun handleBadRequest(ex: BadRequestException, request: WebRequest): ResponseEntity<Any> {
        val message = ex.message
        return ResponseEntity(message, HttpHeaders(), HttpStatus.BAD_REQUEST)
    }
}

这只是为了快速测试一下我是否可以更改服务器响应。结果我做不到,客户端仍然得到相同的响应,尽管处理程序 is 已执行。因此,无论从请求中获取信息,都必须在链条的下游。

有人知道这里发生了什么吗?

【问题讨论】:

    标签: spring-boot executable-jar


    【解决方案1】:

    这是自 Spring Boot 2.3 以来的预期行为,如 here 所述

    在 application.properties 中设置 server.error.include-message=always 可解决此问题。

    【讨论】:

      【解决方案2】:

      可以通过注入自定义的 ErrorController 来配置响应,例如这个:

      @Controller
      class ExampleErrorController(private val errorAttributes: ErrorAttributes) : ErrorController {
      
          private val mapper = ObjectMapper()
      
          @RequestMapping("/error")
          @ResponseBody
          fun handleError(request: HttpServletRequest): String {
              val webRequest = ServletWebRequest(request)
              val error = errorAttributes.getError(ServletWebRequest(request))
      
              // if it's not a 500, include the error message in the response. If it's a 500, better not...
              val errorAttributeOptions = if (error !is HttpServerErrorException.InternalServerError) {
                  ErrorAttributeOptions.defaults().including(ErrorAttributeOptions.Include.MESSAGE)
              } else ErrorAttributeOptions.defaults()
      
              val errorDetails = errorAttributes.getErrorAttributes(
                      webRequest, errorAttributeOptions)
      
              return mapper.writeValueAsString(errorDetails)
          }
      
          override fun getErrorPath(): String = "/error"
      }
      

      注意这里是以ErrorAttributeOptions.defaults()为基线,然后配置进去的东西。看起来这个默认对象就是spring boot提供的默认ErrorController使用的对象,其实就是这个对象不同 取决于我是直接从 gradle/intelij 运行它还是将它构建到一个 jar 中。为什么我找不到,但我验证并确认了行为。我假设这是有意的,尽管没有被广泛记录。

      了解了这一点后,我想知道为什么不能只为应用程序全局配置默认选项对象,而不是提供整个控制器,这在许多情况下就足够了,但看起来不可能此时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-26
        • 2019-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-09
        • 2015-05-21
        相关资源
        最近更新 更多