Как прочитать тело запроса в HandlerInterceptor при загрузке Spring? - PullRequest
0 голосов
/ 06 марта 2020

У меня весенняя загрузка, и мне нужно регистрировать действия пользователя в БД, поэтому я написал HandlerInterceptor:

@Component
public class LogInterceptor implements HandlerInterceptor {
@Autovired
private LogUserActionService logUserActionService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
throws IOException {
    String userName = SecurityContextHolder.getContext().getAuthentication().getName();
    String url = request.getRequestURI();
    String queryString = request.getQueryString() != null ? request.getQueryString() : "";
    String body = "POST".equalsIgnoreCase(request.getMethod()) ? new BufferedReader(new InputStreamReader(request.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator())) : queryString;
    logUserActionService.logUserAction(userName, url, body);
    return true;
}
}

Но в соответствии с этим ответом Получить RequestBody и ResponseBody в HandlerInterceptor"RequestBody может быть прочитан только один раз ", так как я понимаю, что я читаю поток ввода, а затем Spring пытается сделать то же самое, но поток уже прочитан, и я получаю сообщение об ошибке:" Обязательное тело запроса отсутствует ... "

Итак, я пробовал разные способы создания буферизованного потока ввода, например:

HttpServletRequest httpServletRequest = new ContentCachingRequestWrapper(request);
new BufferedReader(new InputStreamReader(httpServletRequest.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator()))

или

InputStream bufferedInputStream = new BufferedInputStream(request.getInputStream());

Но ничего не помогло. Также я попытался использовать

@ControllerAdvice
public class UserActionRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

но у него есть только тело, нет информации о запросе, такой как URL или параметры запроса. Также пытался использовать фильтры, но результат тот же.

Так что мне нужен хороший способ получить информацию из запроса, такую ​​как user, url, параметры, body ( если есть) и запишите его в БД. Как лучше, пожалуйста, помогите!

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Вы можете использовать Filter для регистрации тела запроса.

public class LoggingFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
        try {
            chain.doFilter(wrappedRequest, res);
        } finally {
            logRequestBody(wrappedRequest);
        }
    }

    private static void logRequestBody(ContentCachingRequestWrapper request) {

        byte[] buf = request.getContentAsByteArray();
        if (buf.length > 0) {
            try {
                String requestBody = new String(buf, 0, buf.length, request.getCharacterEncoding());
                System.out.println(requestBody);
            } catch (Exception e) {
                System.out.println("error in reading request body");
            }
        }
    }
}

Здесь важно отметить, что вы должны передать объект ContentCachingRequestWrapper в цепочку фильтров, иначе вы не получите запрос содержимое в нем.

В приведенном выше примере, если вы используете chain.doFilter(req, res) или chain.doFilter(request, res), вы не получите тело запроса в wrappedRequest объекте.

0 голосов
/ 06 марта 2020

Для регистрации HTTP-запросов и ответов вы можете использовать RequestBodyAdviceAdapter и ResponseBodyAdvice. здесь он использует по-моему.

CustomRequestBodyAdviceAdapter. java

@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

    @Autowired
    HttpServletRequest httpServletRequest;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
            Class<? extends HttpMessageConverter<?>> converterType) {

        // here you can full log httpServletRequest and body.

        return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
}

CustomResponseBodyAdviceAdapter. java

@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {

    @Autowired
    private LoggingService loggingService;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
            Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (serverHttpRequest instanceof ServletServerHttpRequest && serverHttpResponse instanceof ServletServerHttpResponse) {

            // here you can full log httpServletRequest and body.
        }
        return o;
    }
}

Выше AdviceAdapter не может обработать GET запрос. Таким образом, вы можете использовать HandlerInterceptor.

CustomWebConfigurerAdapter. java

@Component
public class CustomWebConfigurerAdapter implements WebMvcConfigurer {

   @Autowired
   private CustomLogInterceptor httpServiceInterceptor;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(httpServiceInterceptor);
   }
}

CustomLogInterceptor. java

@Component
public class CustomLogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name()) && request.getMethod().equals(HttpMethod.GET.name())) {

            // here you can full log httpServletRequest and body for GET Request.

        }
        return true;
    }
}

Здесь вы можете сослаться на полный исходный код по моему git.

springboot-http-request-response-loging-with- json -logger

+ Feature => У него уже есть интеграция с ELK ( Elasticsearch, Logsta sh, Kibana)

...