Java 在@RestControllerAdvise上获取请求正文

Java 在@RestControllerAdvise上获取请求正文,java,spring-boot,kotlin,error-handling,Java,Spring Boot,Kotlin,Error Handling,我正在配置一个自定义错误处理,以便将每个错误请求记录到我的API中。 在我的场景中,了解请求中传递的有效负载非常重要,这样我就可以根据需要修复/测试/重新处理它 我已经构建了一个简单的@RestControllerAdvise,但是我很难从中获取请求主体: @RestControllerAdvice class ExceptionHandler { companion object { private val logger = getLogger() }

我正在配置一个自定义错误处理,以便将每个错误请求记录到我的API中。 在我的场景中,了解请求中传递的有效负载非常重要,这样我就可以根据需要修复/测试/重新处理它

我已经构建了一个简单的
@RestControllerAdvise
,但是我很难从中获取请求主体:

@RestControllerAdvice
class ExceptionHandler {

    companion object {
        private val logger = getLogger()
    }

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    fun badRequest(e: Exception, request: HttpServletRequest): Exception {
        logger.error("Received a bad request with body: ${request.getBodyPlease()}", e) // Note getBodyPlease() is not a real method
        return e
    }
}
我已尝试从请求中读取
InputStream
,但此时它已关闭。 建议注入一个
RequestContext
并将其设置为
Controller
。这在
错误请求
场景中不起作用,因为它不会执行控制器。而且,在每个控制器中设置它也没有多大意义

提前感谢,

编辑

正如@BeUndead所建议的,我尝试实现一个过滤器来包装请求:

@Component
class RequestWrapperFilter : Filter {
    override fun doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) {
        val reqWrapper = ContentCachingRequestWrapper(req as HttpServletRequest)
        chain.doFilter(reqWrapper, res)
    }
}
然后,我尝试获取类似于
request.reader.lines().collect(Collectors.joining())
的body。
当我尝试使用
ServletRequest
过滤器时,它工作正常,我看到了主体。但是,当我使用
ContentCachingRequestWrapper
尝试
@ExceptionHandler
时,我得到了一个空字符串,

,正如@BeUndead建议的那样,我实现了一个过滤器,将我的请求包装在
ContentCachingRequestWrapper
中:

@Component
class RequestWrapperFilter : Filter {
    override fun doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) {
        val reqWrapper = ContentCachingRequestWrapper(req as HttpServletRequest)
        chain.doFilter(reqWrapper, res)
    }
}
然后,我可以从
内容AsByteArrayProperty
中随时准备好身体:

@RestControllerAdvice
class ExceptionHandler {

    companion object {
        private val logger = getLogger()
    }

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    fun badRequest(e: Exception, request: HttpServletRequest): Exception {
        val body = (request as ContentCachingRequestWrapper).contentAsByteArray.toString(Charsets.UTF_8)
        logger.error("Received a bad request with body: $body", e)
        return e
    }
}

正如@BeUndead所建议的,我实现了一个过滤器,将我的请求包装在
ContentCachingRequestWrapper
中:

@Component
class RequestWrapperFilter : Filter {
    override fun doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain) {
        val reqWrapper = ContentCachingRequestWrapper(req as HttpServletRequest)
        chain.doFilter(reqWrapper, res)
    }
}
然后,我可以从
内容AsByteArrayProperty
中随时准备好身体:

@RestControllerAdvice
class ExceptionHandler {

    companion object {
        private val logger = getLogger()
    }

    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    fun badRequest(e: Exception, request: HttpServletRequest): Exception {
        val body = (request as ContentCachingRequestWrapper).contentAsByteArray.toString(Charsets.UTF_8)
        logger.error("Received a bad request with body: $body", e)
        return e
    }
}

您能否添加一个
过滤器
,将
HttpServletRequest
包装成
ContentCachingRequestWrapper
,然后从请求中获取
InputStream
?@BeUndead我已尝试实现,但仍然没有正确实现。你能检查一下问题中的编辑吗?@BeUndead发现了。如上所述,即使使用
ContentCachingRequestWrapper
,我们仍然只能从流中读取一次。但是,我们可以通过
contentAsByteArray
属性执行任意次数的操作。谢谢您能否添加一个
过滤器
,将
HttpServletRequest
包装成
ContentCachingRequestWrapper
,然后从请求中获取
InputStream
?@BeUndead我已尝试实现,但仍然没有正确实现。你能检查一下问题中的编辑吗?@BeUndead发现了。如上所述,即使使用
ContentCachingRequestWrapper
,我们仍然只能从流中读取一次。但是,我们可以通过
contentAsByteArray
属性执行任意次数的操作。谢谢很高兴这对你有用。希望缓存请求数据在您的用例中不会占用太多内存。如果结果是这样的话,你可以让过滤器对映射进行选择。是的,我也在考虑这个问题。但是缓存只在该请求的生存期内,对吗?当过滤器链完成执行时,它可用于垃圾收集。但我不认为有任何明确的清理,所以你要等到垃圾收集完成。很高兴这对你有用。希望缓存请求数据在您的用例中不会占用太多内存。如果结果是这样的话,你可以让过滤器对映射进行选择。是的,我也在考虑这个问题。但是缓存只在该请求的生存期内,对吗?当过滤器链完成执行时,它可用于垃圾收集。但我不认为有任何明确的清理,所以你要等到垃圾收集完成。