首页 文章

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

提问于
浏览
15

我使用的是Spring Boot 1.4.1,其中包括spring-web-4.3.3 . 我有一个用 @ControllerAdvice 注释的类和用 @ExceptionHandler 注释的方法来处理服务代码抛出的异常 . 在处理这些异常时,我想记录作为PUT和POST操作请求的一部分的 @RequestBody ,这样我就可以看到导致问题的请求体,在我的情况下,这对于诊断至关重要 .

Spring Docs @ExceptionHandler 方法的方法签名可以包括各种内容,包括 HttpServletRequest . 请求体通常可以通过 getInputStream()getReader() 从这里获得,但是如果我的控制器方法像_1293215那样解析请求体,那么 HttpServletRequest's 输入流或读取器在调用异常处理程序方法时已经关闭 . 本质上,Spring已经读取了请求主体,类似于here描述的问题 . 使用servlet是一个常见问题,请求体只能读取一次 .

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

我可以在异常处理程序方法中添加 InputStream ,但最终与HttpServletRequest的InputStream相同,因此具有相同的问题 .

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

我已经阅读了一些像thisthis这样的解决方案,它们涉及在过滤器链中插入一个自定义请求包装器,它将读取请求的内容并缓存它们,以便可以多次读取它们 . 我不想中断整个过滤器/请求/响应链(并可能引入性能或稳定性问题)只是为了实现日志记录,如果我有任何大的请求,如上传的文件(我这样做),我不想要将其缓存在内存中 . 此外,如果我只能找到它,Spring可能已经在某处缓存了 @RequestBody .

顺便提一下,许多解决方案建议使用 ContentCachingRequestWrapper Spring类,但根据我的经验,这不起作用 . 除了没有记录之外,查看其源代码看起来它只是缓存参数,而不是请求体 . 尝试从此类获取请求正文总是会产生一个空字符串 .

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

2 回答

  • 0

    您可以将请求正文对象引用到请求范围的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;
        }
    }
    
  • 2

    接受的答案会创建一个新的POJO来传递内容,但是通过重用http请求可以在不创建其他对象的情况下实现相同的行为 .

    Controller映射的示例代码:

    public ResponseEntity savePerson(@RequestBody Person person, WebRequest webRequest) {
        webRequest.setAttribute("person", person, RequestAttributes.SCOPE_REQUEST);
    

    稍后在ExceptionHandler类/方法中,您可以使用:

    @ExceptionHandler(Exception.class)
    public ResponseEntity exceptionHandling(WebRequest request,Exception thrown) {
    
        Person person = (Person) request.getAttribute("person", RequestAttributes.SCOPE_REQUEST);
    

相关问题