如何在@ExceptionHandler (Spring REST) 中获取@RequestBody [英] How to get the @RequestBody in an @ExceptionHandler (Spring REST)

查看:91
本文介绍了如何在@ExceptionHandler (Spring REST) 中获取@RequestBody的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using Spring Boot 1.4.1 which includes spring-web-4.3.3. I have a class annotated with @ControllerAdvice and methods annotated with @ExceptionHandler to handle exceptions thrown by the service code. When handling these exceptions, I would like to log the @RequestBody that was part of the request for PUT and POST operations so I can see the request body that caused the problem which in my case is crucial for diagnosis.

每个 Spring文档 @ExceptionHandler 方法的方法签名可以包括各种内容,包括 HttpServletRequest.请求正文通常可以通过 getInputStream()getReader() 从这里获取,但如果我的控制器方法解析请求正文,如 "@RequestBody Foo fooBody" 和我所有人一样,在调用我的异常处理程序方法时,HttpServletRequest 的 输入流或读取器已经关闭.基本上请求正文已经被 Spring 读取,类似于 here.使用 servlet 的一个常见问题是请求正文只能被读取一次.

Per Spring Docs the method signature for @ExceptionHandler methods can include various things including the HttpServletRequest. The request body can normally be obtained from here via getInputStream() or getReader(), but if my controller methods parse the request body like "@RequestBody Foo fooBody" as all of mine do, the HttpServletRequest's input stream or reader is already closed by the time my exception handler method is called. Essentially the request body has already been read by Spring, similar to the issue described here. It is a common problem working with servlets that the request body can only be read once.

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

Unfortunately @RequestBody is not one of the options available for the exception handler method, if it were then I could use that.

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

I can add an InputStream to the exception handler method, but that ends up being the same thing as the HttpServletRequest's InputStream and so has the same issue.

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

I also tried getting the current request with ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest() which is another trick for getting the current request, but this ends up being the same HttpServletRequest that Spring passes into the exception handler method and so has the same problem.

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

I have read about a few solutions like this and this that involve inserting a custom request wrapper in the filter chain that will read the contents of the request and cache them so they can be read more than once. I don't like this solution because I don't want to interrupt the entire filter/request/response chain (and potentially introduce performance or stability problems) just to implement logging, and if I have any large requests such as uploaded documents (which I do), I don't want to cache that in memory. Besides, Spring probably has the @RequestBody cached somewhere already if I could only find it.

顺便说一下,许多解决方案推荐使用 ContentCachingRequestWrapper Spring 类,但根据我的经验,这不起作用.除了没有被记录,查看它的源代码看起来它只缓存参数,而不是请求正文.尝试从此类获取请求正文总是导致空字符串.

Incidentally many solutions recommend using the ContentCachingRequestWrapper Spring class but in my experience this does not work. Aside from not being documented, looking at its source code it looks like it only caches the parameters, but not the request body. Trying to get the request body from this class always results in an empty string.

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

So I am looking for any other options that I may have missed. thanks for reading.

推荐答案

您可以将请求正文对象引用到请求范围的 bean.然后将该请求范围的 bean 注入您的异常处理程序中以检索请求正文(或您希望引用的其他请求上下文 bean).

You can reference the request body object to a request-scoped bean. And then inject that request-scoped bean in your exception handler to retrieve the request body (or other request-context beans that you wish to reference).

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

这篇关于如何在@ExceptionHandler (Spring REST) 中获取@RequestBody的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆