use*_*552 3 java spring spring-mvc spring-boot
我有 Spring Boot,我需要在数据库中记录用户操作,所以我编写了 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;
}
}
Run Code Online (Sandbox Code Playgroud)
但根据这个答案Get RequestBody and ResponseBody at HandlerInterceptor "RequestBody can be read only Once",所以据我了解,我读取输入流,然后 Spring 尝试执行相同的操作,但流已被读取,并且我收到错误: “缺少所需的请求正文......”
所以我尝试了不同的方法来制作缓冲输入流,即:
HttpServletRequest httpServletRequest = new ContentCachingRequestWrapper(request);
new BufferedReader(new InputStreamReader(httpServletRequest.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator()))
Run Code Online (Sandbox Code Playgroud)
或者
InputStream bufferedInputStream = new BufferedInputStream(request.getInputStream());
Run Code Online (Sandbox Code Playgroud)
但没有任何帮助我也尝试使用
@ControllerAdvice
public class UserActionRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
Run Code Online (Sandbox Code Playgroud)
但它只有正文,没有 URL 或请求参数等请求信息 也尝试使用过滤器,但结果相同。
因此,我需要一种好方法来从请求中获取信息,如用户、URL、参数、正文(如果存在)并将其写入数据库。
要记录 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);
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
上面AdviceAdapter无法处理该GET请求。所以,你可以使用HandlerInterceptor.
CustomWebConfigurerAdapter.java
@Component
public class CustomWebConfigurerAdapter implements WebMvcConfigurer {
@Autowired
private CustomLogInterceptor httpServiceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpServiceInterceptor);
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在这里你可以参考我的 git 中的完整源代码。
springboot-http-request-response-logging-with-json-logger
+功能=>它已经与ELK集成(Elasticsearch,Logstash,Kibana)
您可以用来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");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里要注意的主要事情是,您必须ContentCachingRequestWrapper在过滤器链中传递对象,否则您将无法在其中获取请求内容。
在上面的示例中,如果您使用chain.doFilter(req, res)或chain.doFilter(request, res)那么您将不会在wrappedRequest对象中获得请求正文。
| 归档时间: |
|
| 查看次数: |
23579 次 |
| 最近记录: |