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

新手上路,请多包涵

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

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

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

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

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

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

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

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

原文由 Uncle Long Hair 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.2k
2 个回答

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

原文由 Warren Nocos 发布,翻译遵循 CC BY-SA 3.0 许可协议

接受的答案会创建一个新的 POJO 来传递东西,但是可以通过重用 http 请求来实现相同的行为,而无需创建其他对象。

控制器映射的示例代码:

 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);

原文由 WeMakeSoftware 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题