【问题标题】:Get request body on @RestControllerAdvise在 @RestControllerAdvise 上获取请求正文
【发布时间】:2020-05-29 08:39:34
【问题描述】:

我正在配置自定义错误处理,以便将每个错误请求记录到我的 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,但此时它已经关闭。 Another question 建议注入 RequestContext 并将其设置为 Controller。这在Bad Request 场景中不起作用,因为它不会执行控制器。此外,在每个控制器中设置它也没有多大意义。

提前致谢,

编辑

正如@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())。 当我尝试使用过滤器时,ServletRequest 工作正常,我看到了身体。但是当我尝试使用 @ExceptionHandlerContentCachingRequestWrapper 时,我得到一个空字符串,

【问题讨论】:

  • 你能添加一个Filter,它将HttpServletRequest包装成ContentCachingRequestWrapper,然后然后从请求中获取InputStream吗?
  • @BeUndead 我已经尝试过实施,但仍然没有做好。您可以检查问题中的编辑吗?
  • @BeUndead 发现了。如here 所述,即使使用ContentCachingRequestWrapper,我们仍然只能从流中读取一次。但是我们可以从contentAsByteArray 属性中执行任意多次。谢谢!

标签: java spring-boot kotlin error-handling


【解决方案1】:

按照@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)
    }
}

然后我可以通过contentAsByteArrayProperty 随时准备好身体:

@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
    }
}

【讨论】:

  • 很高兴这对你有用。希望在您的用例中缓存请求数据不会太占用内存。如果结果是这样,您总是可以让过滤器对映射有选择性。
  • 是的,我也在考虑这个问题。但是缓存只针对该请求的生命周期,对吧?
  • 过滤链执行完毕后即可进行垃圾回收。但我不认为有任何明确的清理,所以你要等到垃圾收集完成。
猜你喜欢
  • 2015-07-10
  • 2017-09-03
  • 2015-11-25
  • 1970-01-01
  • 2021-01-14
  • 2018-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多