我正在使用Spring Boot 1.4.1,其中包括spring-web-4.3.3。我有一个带有@ControllerAdvice注释的类和带有注释的方法,@ExceptionHandler用于处理服务代码引发的异常。处理这些异常时,我想记录@RequestBodyPUT和POST操作请求的一部分,因此我可以看到导致问题的请求正文,在我看来,该问题对于诊断至关重要。
spring-web-4.3.3
@ControllerAdvice
@ExceptionHandler
@RequestBodyPUT
在Spring Docs中,方法的方法签名@ExceptionHandler可以包括多种内容HttpServletRequest。通常可以从此处通过getInputStream()或获取请求正文getReader(),但是如果我的控制器方法像"@RequestBody Foo fooBody"我的所有方法一样解析请求正文,则在HttpServletRequest's调用我的异常处理程序方法时,输入流或阅读器已经关闭。本质上,Spring已经读取了请求正文,类似于此处描述的问题。使用servlet是一个常见问题,即请求正文只能被读取一次。
HttpServletRequest
getInputStream()
getReader()
"@RequestBody Foo fooBody"
HttpServletRequest's
不幸的@RequestBody是,不是异常处理程序方法可用的选项之一,如果可以的话,我可以使用它。
@RequestBody
我可以InputStream在异常处理程序方法中添加,但是最终与HttpServletRequest的InputStream相同,因此存在相同的问题。
InputStream
我也尝试获取当前请求,((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()这是获取当前请求的另一个技巧,但这最终与Spring传递给异常处理程序方法的HttpServletRequest相同,因此也存在相同的问题。
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
我看了一下像一些解决方案,这个和这个涉及插入过滤器链将读取请求的内容,并缓存它们,所以它们可以被读取一次以上的自定义请求包装。我不喜欢这种解决方案,因为我不想为了实现日志记录而中断整个过滤器/请求/响应链(并可能引入性能或稳定性问题),并且如果我有任何大请求,例如上载文档(我知道),我不想将其缓存在内存中。此外,@RequestBody如果我只能找到的话,Spring可能已经在某个地方缓存了。
顺便说一句,许多解决方案建议使用ContentCachingRequestWrapperSpring类,但是根据我的经验,这是行不通的。除了没有文档记录之外,查看其源代码看起来还只是缓存参数,而不缓存请求正文。尝试从此类获取请求正文始终会导致一个空字符串。
ContentCachingRequestWrapperSpring
所以我正在寻找我可能错过的任何其他选择。谢谢阅读。
你可以将请求主体对象引用到请求范围的Bean。然后将该请求范围的Bean注入你的异常处理程序中以检索请求主体(或你希望引用的其他请求上下文Bean)。
// @Component // @Scope("request") @ManagedBean @RequestScope public class RequestContext { // fields, getters, and setters for request-scoped beans } @RestController @RequestMapping("/api/v1/persons") public class PersonController { @Inject private RequestContext requestContext; @Inject private PersonService personService; @PostMapping public Person savePerson(@RequestBody Person person) throws PersonServiceException { requestContext.setRequestBody(person); return personService.save(person); } } @ControllerAdvice public class ExceptionMapper { @Inject private RequestContext requestContext; @ExceptionHandler(PersonServiceException.class) protected ResponseEntity<?> onPersonServiceException(PersonServiceException exception) { Object requestBody = requestContext.getRequestBody(); // ... return responseEntity; } }