Spring-Boot 从 ErrorAttributes 中删除消息作为响应

Spring-Boot removes message from ErrorAttributes in response

当从 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 中配置,那样会很方便。问题是,我没有配置它。我将 运行ning 服务器的配置属性与本地服务器的配置属性进行了比较,但我没有看到任何可能对此负责的 属性。如果我 google 也找不到。根据我找到的所有教程,这应该可以正常工作。它有点像,除了不在 运行ning 服务器上。

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

附加信息:

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

我已经尝试通过 ControllerAdvice return 我自己的错误响应:

@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 已执行。因此,无论从请求中提取信息的是什么,都必须进一步向下延伸。

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

可以通过注入自定义 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()为基准,然后配置进入的内容。看来这个默认对象是默认ErrorController spring boot提供的使用的对象,它在事实上,这个对象 不同 取决于我是 运行 直接从 gradle/intelij 获取它还是将其构建到 jar 中。为什么我找不到,但我验证并确认了这种行为。我假设它是有意的,尽管没有广泛记录。

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

这是自 Spring Boot 2.3 以来的预期行为

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