一尘不染

如何在@ExceptionHandler(Spring REST)中获取@RequestBody

spring

我正在使用Spring Boot 1.4.1,其中包括spring-web-4.3.3。我有一个带有@ControllerAdvice注释的类和带有注释的方法,@ExceptionHandler用于处理服务代码引发的异常。处理这些异常时,我想记录@RequestBodyPUT和POST操作请求的一部分,因此我可以看到导致问题的请求正文,在我看来,该问题对于诊断至关重要。

在Spring Docs中,方法的方法签名@ExceptionHandler可以包括多种内容HttpServletRequest。通常可以从此处通过getInputStream()或获取请求正文getReader(),但是如果我的控制器方法像"@RequestBody Foo fooBody"我的所有方法一样解析请求正文,则在HttpServletRequest's调用我的异常处理程序方法时,输入流或阅读器已经关闭。本质上,Spring已经读取了请求正文,类似于此处描述的问题。使用servlet是一个常见问题,即请求正文只能被读取一次。

不幸的@RequestBody是,不是异常处理程序方法可用的选项之一,如果可以的话,我可以使用它。

我可以InputStream在异常处理程序方法中添加,但是最终与HttpServletRequestInputStream相同,因此存在相同的问题。

我也尝试获取当前请求,((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()这是获取当前请求的另一个技巧,但这最终与Spring传递给异常处理程序方法的HttpServletRequest相同,因此也存在相同的问题。

我看了一下像一些解决方案,这个和这个涉及插入过滤器链将读取请求的内容,并缓存它们,所以它们可以被读取一次以上的自定义请求包装。我不喜欢这种解决方案,因为我不想为了实现日志记录而中断整个过滤器/请求/响应链(并可能引入性能或稳定性问题),并且如果我有任何大请求,例如上载文档(我知道),我不想将其缓存在内存中。此外,@RequestBody如果我只能找到的话,Spring可能已经在某个地方缓存了。

顺便说一句,许多解决方案建议使用ContentCachingRequestWrapperSpring类,但是根据我的经验,这是行不通的。除了没有文档记录之外,查看其源代码看起来还只是缓存参数,而不缓存请求正文。尝试从此类获取请求正文始终会导致一个空字符串。

所以我正在寻找我可能错过的任何其他选择。谢谢阅读。


阅读 1417

收藏
2020-04-18

共1个答案

一尘不染

你可以将请求主体对象引用到请求范围的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;
    }
}
2020-04-18