春季阅读请求机构两次

时间:2015-09-24 14:25:19

标签: spring spring-mvc logging

在春天,我有一个控制器,其端点如下:

Here is the code that I used 

function myFunction(e){

var userName = e.values[1];
var userEmail = e.values[2];
var score = e.values[3];
var subject = "Thank you for your participation: Find your Score";
var message = "Thank you, " + userName + " for choosing to participate in   this test. Your score is " +score;
MailApp.sendEmail (userEmail, subject, message);}

这样,如果在此端点上执行POST,请求正文中的JSON将自动反序列化为我的模型(@RequestMapping(method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) @ResponseBody public OutputStuff createStuff(@RequestBody Stuff stuff) { //my logic here } )。问题是,我刚要求记录原始JSON,因为它正在进入!我尝试了不同的方法。

  1. Stuff注入HttpServletRequest,在那里阅读正文并记录:
  2. 代码:

    createStuff

    这个问题是,当我执行它时,读者的InputStream已经被执行以将JSON反序列化为@RequestMapping(method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) @ResponseBody public OutputStuff createStuff(@RequestBody Stuff stuff, HttpServletRequest req) { StringBuilder sb = new StringBuilder(); req.getReader().getLines().forEach(line -> { sb.append(line); }); //log sb.toString(); //my logic here } 。所以我会收到一个错误,因为我无法读取相同的输入流两次。

    1. 使用在调用实际处理程序之前记录原始JSON的自定义Stuff
    2. 代码(部分内容):

      HandlerInterceptorAdapter

      这个问题是,当反序列化到public class RawRequestLoggerInterceptor extends HandlerInterceptorAdapter { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { StringBuilder sb = new StringBuilder(); req.getReader().getLines().forEach(line -> { sb.append(line); }); //log sb.toString(); return true; } } 时,请求的InputStream已经被读取了!所以我会再次得到一个例外。

      1. 我考虑的另一个选项,但尚未实现,将以某种方式迫使Spring使用我的stuff自定义实现,它将缓存输入流并允许多次读取它。我不知道这是否可行,我无法找到任何文档或示例!

      2. 另一种选择是不在我的端点上阅读HttpServletRequest,而是将请求正文读作Stuff,记录它,然后使用{将其反序列化为String {1}}或类似的东西。我不喜欢这个想法。

      3. 有没有更好的解决方案,我没有提及和/或我不知道?我很感激帮助。我正在使用最新版本的SpringBoot。

2 个答案:

答案 0 :(得分:3)

正如本文所述:How to Log HttpRequest and HttpResponse in a file?,spring提供了可用于记录请求的AbstractRequestLoggingFilter。

AbstractRequestLoggingFilter API文档,found here

答案 1 :(得分:0)

对于read the request body multiple times,我们必须缓存初始有效负载。因为一旦使用完原始的 InputStream ,我们就无法再次读取它。

首先,Spring MVC提供了 ContentCachingRequestWrapper 类,用于存储原始内容。因此,我们可以调用 getContentAsByteArray()方法多次检索正文。

因此,在您的情况下,可以在过滤器中使用此类:

@Component
public class CachingRequestBodyFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
  throws IOException, ServletException {
        HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
        // Other details

        chain.doFilter(wrappedRequest, servletResponse);
    }
}

或者,您可以在应用程序中注册 CommonsRequestLoggingFilter 。此过滤器在幕后使用 ContentCachingRequestWrapper ,并且是为logging the requests设计的。