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

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

问题描述

我正在使用Spring Boot 1.4.1,其中包括spring-web-4.3.3。我有一个用 @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 Docs @ExceptionHandler 方法的方法签名可以包括各种内容,包括 HttpServletRequest 。通常可以通过 getInputStream() getReader()从此处获取请求正文,但是如果我的控制器方法可以解析像 @ RequestBody Foo fooBody 这样的请求正文, HttpServletRequest的输入流或阅读器已经在我的异常处理程序方法被调用时关闭。本质上,Spring已经读取了请求正文,类似于在此处所述的问题。一个>。使用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()的请求,这是获取当前请求的另一个技巧,但这最终与Spring传递的HttpServletRequest相同

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.

我已经阅读了一些解决方案,例如,其中涉及在过滤器链中插入自定义请求包装器,该包装器将读取其中的内容请求并缓存它们,以便可以多次读取它们。我不喜欢这种解决方案,因为我不想为了实现日志记录而中断整个过滤器/请求/响应链(并可能引入性能或稳定性问题),并且如果我有任何大请求,例如上载文档(我知道),我不想将其缓存在内存中。此外,如果我只能找到的话,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天全站免登陆