当前位置: 首页 > 知识库问答 >
问题:

RestController ExceptionHandler中的日志请求正文字符串

阙俊友
2023-03-14
    < li >最终目标:

RestController的@ExceptionHandler中的日志请求正文字符串。

  • 解释

默认情况下,当请求是无效的json时,springboot会抛出一个< code > httpmessagenoretreadableexception ,但该消息非常通用,并不包括具体的请求体。这使得调查变得困难。另一方面,我可以使用过滤器记录每个请求字符串,但是这样日志会被太多成功的请求淹没。我只想在请求无效时记录请求。我真正想要的是在< code>@ExceptionHandler中,我将获取该字符串(之前在某个地方获取的)并记录为错误。

为了说明这个问题,我在github中创建了一个演示项目。

    < li >控制器:
@RestController
public class GreetController {
    protected static final Logger log = LogManager.getLogger();

    @PostMapping("/")
    public String greet(@RequestBody final WelcomeMessage msg) {
        // if controller successfully returned (valid request),
        // then don't want any request body logged
        return "Hello " + msg.from;
    }

    @ExceptionHandler({HttpMessageNotReadableException.class})
    public String addStudent(HttpMessageNotReadableException e) {
        // this is what I really want!
        log.error("{the request body string got somewhere, such as Filters }"); 
        return "greeting from @ExceptionHandler";
    }
}

>

  • 客户端
    1. 有效请求curl-H"Content-Type: application/json"http://localhost:8080--data'{"from":"jim","message":"很高兴认识你!"}'

      无效请求(无效JSON)< code > curl-H " Content-Type:application/JSON " http://localhost:8080-data ' { " from ":" Jim "," message " "幸会!"}'

      我曾经尝试过HandlerInterceptor,但会出现如下错误

      java.lang.IllegalStateException:在为当前请求调用getReader()后,无法调用getInputStream()。

      经过一些搜索1 2,我决定使用过滤器内容缓存请求包装器

      java prettyprint-override">    @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
              ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest);
              chain.doFilter(cachedRequest, response);
              String requestBody = IOUtils.toString(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding());
              log.info(requestBody);
          }
      

      此代码运行良好,只是日志在RestController之后。如果我更改订单:

              String requestBody = IOUtils.toString(cachedRequest.getReader());
              log.info(requestBody);
              chain.doFilter(cachedRequest, response);
      

      适用于无效请求,但当请求有效时,出现以下异常:

      com示例.demo。GreetController:缺少必需的请求正文:public java.lang.String com.example.demo.GreetContrroller.greet(com.exaample.demo.WelcomeMessage)

      我还尝试了获取内容数组获取输入流getReader 方法,因为一些教程说框架会检查特定的方法调用。

      按照@M的建议尝试了CommonsRequestLoggingFilter。戴恩。

      但都是徒劳。

      现在我有点困惑。有人能解释一下请求有效和无效时RestControllerFilter的执行顺序吗?有什么更简单的方法(更少的代码)来实现我的最终目标?谢谢!

      我使用的是Spring靴 2.6.3, jdk11.

  • 共有2个答案

    萧永望
    2023-03-14

    跟随@M.Deinum的评论,我解决了并希望对别人有用:

    1. 添加筛选器
    public class MyFilter implements Filter {
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest);
        chain.doFilter(cachedRequest, response);
      }
    }
    
      @ExceptionHandler({ HttpMessageNotReadableException.class })
      public String addStudent(HttpMessageNotReadableException e, ContentCachingRequestWrapper cachedRequest) {
        log.error(e.getMessage());
        try {
          String requestBody = IOUtils.toString(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding());
          log.error(requestBody);
        } catch (IOException ex) {
          ex.printStackTrace();
        }
        return "greeting from @ExceptionHandler";
      }
    
    金钧
    2023-03-14

    >

  • 创建一个过滤器,将您的请求包装在ContentCachingRequestWrapper中(无关紧要)。

    使用HttpServletRequest作为异常处理方法中的参数作为参数

    检查ContentCachingRequestWrapper的实例

    使用获取内容字节数组来获取内容。

    像这样。

    public class CachingFilter extends OncePerRequestFilter {
    
    protected abstract void doFilterInternal(
                HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
    
      filterChain.doFilter(new ContentCachingRequestWrapper(request), new ContentCachingResponseWrapper(response));
    }
    

    注意:我也包装了响应,以防万一你也想要它。

    现在,在您的异常处理方法中使用< code>HttpServletRequest作为参数,并充分利用这一点。

    @ExceptionHandler({HttpMessageNotReadableException.class})
    public String addStudent(HttpMessageNotReadableException e, HttpServletRequest req) {
      if (req instanceof ContentCachingRequestWrapper) {
        ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req;
        log.error(new String(wrapper.getContentAsByteArray()));
      }
      return "greeting from @ExceptionHandler";
    }
    

    可能是多个过滤器将包装器添加到HttpServletRequest,因此您可能需要迭代这些包装器,您也可以使用

    private Optional<ContentCachingRequestWrapper> findWrapper(ServletRequest req) {
        ServletRequest reqToUse = req;
        while (reqToUse instanceof ServletRequestWrapper) {
            if (reqToUse instanceof ContentCachingRequestWrapper) {
                return Optional.of((ContentCachingRequestWrapper) reqToUse);
            }
            reqToUse = ((ServletRequestWrapper) reqToUse).getRequest();
        }
        return Optional.empty();
    }
    

    然后,您的异常处理程序将如下所示

    @ExceptionHandler({HttpMessageNotReadableException.class})
    public String addStudent(HttpMessageNotReadableException e, HttpServletRequest req) {
      Optional<ContentCachingRequestWrapper) wrapper = findWrapper(req);
      wrapper.ifPresent(it -> log.error(new String(it.getContentAsByteArray())));
     
      return "greeting from @ExceptionHandler";
    }
    

    但这可能取决于您的过滤器顺序以及是否有多个过滤器添加包装器。

  •  类似资料:
    • Webpack开发服务器代理配置留档: https://webpack.js.org/configuration/dev-server/#devserver-代理 表示它使用http代理中间件: https://github.com/chimurai/http-proxy-middleware#http-代理事件 使用上述链接中记录的功能,我可以执行以下操作: 我的问题是,尽管其他一切都很好——我

    • 我使用的是spring boot Version-2.0.6.Release和spring cloud Version-finchley.sr2

    • 我在Java中使用play框架。我想检索在POST请求中发送到play服务器的整个请求正文。我怎样才能取回它?

    • 问题内容: 我正在将带有JSON正文的POST请求发送到Django服务器(相当标准)。在服务器上,我需要使用进行解码。 问题是如何获取字符串格式的请求正文? 我目前有以下代码: 但是,这给出了一个错误。 如何以正确的编码将请求的主体作为字符串检索? 问题答案: 请求主体,是一个字节字符串。在Python 3中,将仅接受unicode字符串,因此您必须先进行解码,然后再将其传递给。 在Python

    • 使用Grails 4构建一个新的API,我希望可以选择记录完整的请求(标头、方法、内容等)。我可以在Interceptor中看到请求,但是内容只能读取一次(使用),所以在Interceptor中读取它会阻止内容在Controller中可用。 在堆栈溢出中已经有一些类似的问题,通过使用Grails过滤器来解决这个需求。 我不想走这条路的一个原因是,根据Grails文档,过滤器现在被认为是不建议使用的

    • 我试图在flask应用程序上记录post请求,如下所示: 我这样做是因为我怀疑有时我会在POST上得到一个格式错误的JSON对象。我看到的问题是日志包含一个截断的请求体——它似乎被夹在中间... 我不能放日志摘要,因为它是敏感数据。。为什么请求被截断?我做错了吗? 谢谢