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

问题描述

从spring-boot控制器引发异常时,服务器响应中的消息为空-但仅当我不在本地运行时才如此。最后一部分是让我最困惑的地方。我的意思是,让spring-boot从错误响应中删除部件将是非常有意义的。例如,像stacktrace一样,除了调试时,没有人希望将其发送出去。

但是,当我在本地运行应用程序时,会得到完整的错误响应,消息,堆栈跟踪以及所有内容(即使不是在调试模式下运行,我最初怀疑这可能是原因)。一个典型的错误可能看起来像这样:

{
    "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","message": "","path": "/users/9"
}

我根本不介意删除stacktrace(实际上我希望将其删除),但是我真的很想收到该错误消息。

我曾经以为它可能与跨域访问有关,但是当通过swagger而不是前端产生错误时,我会得到相同的行为,并且swagger是同源的。

我完全希望这种行为在spring-boot中是可配置的,这很方便。麻烦的是,我没有配置它。我将正在运行的服务器的配置属性与本地服务器的配置属性进行了比较,但看不到任何可能导致该问题的属性。如果我用Google搜索,也找不到任何内容。根据我发现的所有教程,这应该可以正常工作。它可以做什么,除了不在运行的服务器上。

有人知道在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)
    }
}

这只是一个快速测试,目的是查看我是否可以更改服务器响应。事实证明我不能,尽管执行了处理程序 ,客户​​端仍然得到相同的响应。因此,将信息从请求中移出的任何方式都必须走得更远。

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

解决方法

可以通过注入自定义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中。为什么我找不到,但是我验证并确认了这种行为。我假设是有意的,尽管没有被广泛记录。

一旦我学会了这一点,我想知道为什么不可能仅为应用程序全局配置默认的Options对象,而不是提供整个控制器,这在很多情况下就足够了,但看起来不可能此时。

,

自Spring Boot 2.3起,这是预期的行为,如here

所述

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