经过多次尝试,我在这里询问是否有一种方法可以在将输入 JSON 请求反序列化为 Java 对象之前记录它。
我的尝试:
1: 在反序列化之前,我能够正确记录请求,但随后给出“读取输入消息时出现 I/O 错误”
代码:RequestInterceptor(从HttpServletRequest获取RequestBody)
@Slf4j
@Order(3)
@Component
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(final HttpServletRequest request,
final HttpServletResponse response,
final Object handler) throws Exception {
logRequestDetails(request);
return true;
}
@SuppressWarnings("unchecked")
private void logRequestDetails(HttpServletRequest request) throws IOException {
String httpMethod = request.getMethod();
String url = request.getRequestURL().toString();
String queryString = request.getQueryString();
Map<String, String> pathVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String requestBody = getRequestBody(request);
log.info("REQUEST INFO: [ (Method: {}) (URL: {}) (Query String: {}) (Path Variables: {}) ]", httpMethod, url, queryString, pathVariables);
log.info("REQUEST: {}", requestBody);
}
private String getRequestBody(HttpServletRequest request) {
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader bufferedReader = request.getReader()) {
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
return "[Error Reading Body]";
}
return stringBuilder.toString();
}
}
日志:HttpMessageNotReadableException:读取输入消息时出现 I/O 错误
2024-07-10 00:43:00.523 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 90963499-0c12-43e3-bd03-f92392090e26 | 16c64a15-6383-4ca2-87ae-34516435f64f | REQUEST INFO: [ (Method: POST) (URL: http://localhost:9001/api/account/passengers/associate-passengers) (Query String: null) (Path Variables: {}) ]
2024-07-10 00:43:00.523 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 90963499-0c12-43e3-bd03-f92392090e26 | 16c64a15-6383-4ca2-87ae-34516435f64f | REQUEST: [ { "accountId": 100, "firstName": "David", "lastName": "Guetta", "dateOfBirth": "1967-11-07", "nationalityId": 63, "email": "[email protected]", "phoneNumber": "+33 3334448899" }]
2024-07-10 00:43:00.654 | http-nio-9001-exec-1 | ERROR | i.g.p.coa.rese.adapt.inbound.controller.rest.ExceptionRestController | 90963499-0c12-43e3-bd03-f92392090e26 | 16c64a15-6383-4ca2-87ae-34516435f64f | Exception 238abf96-75f5-47c2-88dc-93c4ef83e935
org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:200) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:159) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:134) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:228) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:182) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:920) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.19.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.19.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
2: 我能够在反序列化之前正确记录请求,但随后给我“请求正文丢失”
代码:RequestInterceptor(请求包装到ContentCachingRequestWrapper中)
@Slf4j
@Order(3)
@Component
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(final HttpServletRequest request,
final HttpServletResponse response,
final Object handler) throws Exception {
ContentCachingRequestWrapper requestWrapper;
if (request instanceof ContentCachingRequestWrapper contentCachingRequestWrapper) {
requestWrapper = contentCachingRequestWrapper;
} else {
requestWrapper = new ContentCachingRequestWrapper(request);
}
logRequestDetails(requestWrapper);
return true;
}
@SuppressWarnings("unchecked")
private void logRequestDetails(ContentCachingRequestWrapper request) throws IOException {
String httpMethod = request.getMethod();
String url = request.getRequestURL().toString();
String queryString = request.getQueryString();
Map<String, String> pathVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String requestBody = getRequestBody(request);
log.info("REQUEST INFO: [ (Method: {}) (URL: {}) (Query String: {}) (Path Variables: {}) ]", httpMethod, url, queryString, pathVariables);
log.info("REQUEST: {}", requestBody);
}
private String getRequestBody(HttpServletRequest request) {
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader bufferedReader = request.getReader()) {
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
return "[Error Reading Body]";
}
return stringBuilder.toString();
}
}
日志:HttpMessageNotReadableException:缺少必需的请求正文
2024-07-10 00:35:34.738 | http-nio-9001-exec-2 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 176cc195-73ec-4921-8d84-5d50f5853cf6 | 5d3d84a5-5330-4608-bc6b-e90916543c94 | REQUEST INFO: [ (Method: POST) (URL: http://localhost:9001/api/account/passengers/associate-passengers) (Query String: null) (Path Variables: {}) ]
2024-07-10 00:35:34.738 | http-nio-9001-exec-2 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 176cc195-73ec-4921-8d84-5d50f5853cf6 | 5d3d84a5-5330-4608-bc6b-e90916543c94 | REQUEST: [ { "accountId": 100, "firstName": "David", "lastName": "Guetta", "dateOfBirth": "1967-11-07", "nationalityId": 63, "email": "[email protected]", "phoneNumber": "+33 3334448899" }]
2024-07-10 00:35:34.758 | http-nio-9001-exec-2 | ERROR | i.g.p.coa.rese.adapt.inbound.controller.rest.ExceptionRestController | 176cc195-73ec-4921-8d84-5d50f5853cf6 | 5d3d84a5-5330-4608-bc6b-e90916543c94 | Exception 6e9a77ed-24bf-438d-934c-4c7981c6bb70
org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public io.github.paulmarcelinbejan.davinci.adapters.api.DavinciApiResponse<java.util.List<io.github.paulmarcelinbejan.coandaairlines.reservationsystem.ports.domain.Passenger>> io.github.paulmarcelinbejan.coandaairlines.reservationsystem.adapters.inbound.controller.rest.PassengerRestController.associatePassengers(java.util.List<io.github.paulmarcelinbejan.coandaairlines.reservationsystem.adapters.inbound.dto.request.PassengerRequest>)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:162) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:134) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:228) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:182) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:920) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:830) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.4.jar:6.1.4]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.19.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.4.jar:6.1.4]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.19.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.4.jar:6.1.4]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.4.jar:6.1.4]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
3: 使用 LoggingFilterBean 我可以记录请求,但它是在 api 末尾记录的,而不是在调用 api 时记录的,而且,来自 MappedDiagnosticContext (
| 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a |
) 编写的 2 个 id拦截器未记录。
代码:LoggingFilterBean
@Slf4j
@Component
public class LoggingFilterBean extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = requestWrapper(request);
ContentCachingResponseWrapper responseWrapper = responseWrapper(response);
chain.doFilter(requestWrapper, responseWrapper);
logRequest(requestWrapper);
logResponse(responseWrapper);
}
private void logRequest(ContentCachingRequestWrapper request) {
StringBuilder builder = new StringBuilder();
final String jsonRequest = new String(request.getContentAsByteArray());
builder.append(jsonRequest);
log.info("Request: {}", builder);
}
private void logResponse(ContentCachingResponseWrapper response) throws IOException {
StringBuilder builder = new StringBuilder();
builder.append(new String(response.getContentAsByteArray()));
log.info("Response: {}", builder);
response.copyBodyToResponse();
}
private ContentCachingRequestWrapper requestWrapper(ServletRequest request) {
if (request instanceof ContentCachingRequestWrapper requestWrapper) {
return requestWrapper;
}
return new ContentCachingRequestWrapper((HttpServletRequest) request);
}
private ContentCachingResponseWrapper responseWrapper(ServletResponse response) {
if (response instanceof ContentCachingResponseWrapper responseWrapper) {
return responseWrapper;
}
return new ContentCachingResponseWrapper((HttpServletResponse) response);
}
}
日志:
2024-07-10 01:00:21.665 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | REQUEST INFO: [ (Method: POST) (URL: http://localhost:9001/api/account/passengers/associate-passengers) (Query String: null) (Path Variables: {}) ]
2024-07-10 01:00:21.745 | http-nio-9001-exec-1 | DEBUG | o.h.SQL | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | select nextval('id_passenger_seq')
2024-07-10 01:00:21.768 | http-nio-9001-exec-1 | DEBUG | o.h.SQL | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | insert into passenger (fk_account,date_of_birth,email,first_name,is_primary,last_name,fk_nationality,phone_number,id_passenger) values (?,?,?,?,?,?,?,?,?)
2024-07-10 01:00:21.770 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (1:BIGINT) <- [9]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (2:VARCHAR) <- [1967-11-07]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (3:VARCHAR) <- [[email protected]]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (4:VARCHAR) <- [David]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (5:BOOLEAN) <- [false]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (6:VARCHAR) <- [Guetta]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (7:INTEGER) <- [63]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (8:VARCHAR) <- [+33 3334448899]
2024-07-10 01:00:21.771 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a | binding parameter (9:BIGINT) <- [23]
2024-07-10 01:00:21.797 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.filter.LoggingFilterBean | | | Request: [
{
"accountId": 9,
"firstName": "David",
"lastName": "Guetta",
"dateOfBirth": "1967-11-07",
"nationalityId": 63,
"email": "[email protected]",
"phoneNumber": "+33 3334448899"
}
]
2024-07-10 01:00:21.797 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.filter.LoggingFilterBean | | | Response: {"status":"OK","response":[{"id":23,"accountId":9,"isPrimary":false,"firstName":"David","lastName":"Guetta","dateOfBirth":"1967-11-07","nationalityId":63,"email":"[email protected]","phoneNumber":"+33 3334448899"}]}
为了能够在调用 API 之前进行记录并且不出现异常,一种解决方案是使用 ControllerAdvice:
4: 请求已经反序列化,因此为了将 json 记录为字符串,我必须序列化请求。
代码:RequestBodyControllerAdvice
@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
public class RequestBodyControllerAdvice extends RequestBodyAdviceAdapter {
private final 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) {
String json;
if (body instanceof JsonSerializer object) {
json = object.toJSON();
} else {
try {
json = OBJECT_MAPPER.writeValueAsString(body);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
log.info("REQUEST: {}", json);
return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
}
日志:
2024-07-10 01:31:55.879 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.interceptor.RequestInterceptor | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | REQUEST INFO: [ (Method: POST) (URL: http://localhost:9001/api/account/passengers/associate-passengers) (Query String: null) (Path Variables: {}) ]
2024-07-10 01:31:55.902 | http-nio-9001-exec-1 | INFO | i.g.p.dav.adap.inbou.rest.filter.RequestBodyControllerAdvice | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | REQUEST: [{"accountId":9,"firstName":"David","lastName":"Guetta","dateOfBirth":"1967-11-07","nationalityId":63,"email":"[email protected]","phoneNumber":"+33 3334448899"}]
2024-07-10 01:31:55.966 | http-nio-9001-exec-1 | DEBUG | o.h.SQL | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | select nextval('id_passenger_seq')
2024-07-10 01:31:55.988 | http-nio-9001-exec-1 | DEBUG | o.h.SQL | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | insert into passenger (fk_account,date_of_birth,email,first_name,is_primary,last_name,fk_nationality,phone_number,id_passenger) values (?,?,?,?,?,?,?,?,?)
2024-07-10 01:31:55.989 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (1:BIGINT) <- [9]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (2:VARCHAR) <- [1967-11-07]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (3:VARCHAR) <- [[email protected]]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (4:VARCHAR) <- [David]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (5:BOOLEAN) <- [false]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (6:VARCHAR) <- [Guetta]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (7:INTEGER) <- [63]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (8:VARCHAR) <- [+33 3334448899]
2024-07-10 01:31:55.990 | http-nio-9001-exec-1 | TRACE | o.h.o.jdb.bind | 0137fbd6-a64c-4b7c-9930-4225a6651051 | 7ed2fa14-ffcf-468e-82ff-a7f5a5bfddae | binding parameter (9:BIGINT) <- [25]
我正在寻找解决方案:
我记得我也遇到过同样的问题。基本上,问题是如果您使用输入流,您将得到:
Required request body is missing after reading request
,因为输入流只能被消耗一次。
@Slf4j
@Component
public class LoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
var cachedRequest = new CachedBodyHttpServletRequest(request);
log.info("PROCESSING REQUEST: " + request.getMethod() + " " + request.getRequestURI()
+ getParameters(request));
filterChain.doFilter(cachedRequest, response);
}
@SneakyThrows
private String getParameters(HttpServletRequest request) {
String body = new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
return body.isEmpty() ? "" : ", with following body: " + body;
}
}
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new CachedBodyServletInputStream(this.cachedBody);
}
@Override
public BufferedReader getReader() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
return new BufferedReader(new InputStreamReader(byteArrayInputStream));
}
}
相对直接的类,将 inputStream 保存到字节数组中,并允许您通过返回 CachedBodyServletInputStream 的 ServletInputStream 实现来获取InputStream。
public class CachedBodyServletInputStream extends ServletInputStream {
private final InputStream cachedBodyInputStream;
public CachedBodyServletInputStream(byte[] cachedBody) {
this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
}
@Override
public int read() throws IOException {
return cachedBodyInputStream.read();
}
@SneakyThrows
@Override
public boolean isFinished() {
return cachedBodyInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new RuntimeException("Breaks Liskov`s substitution principle :(");
}
}
这门课也比较容易上手。它覆盖了所有必要的方法,但也必须覆盖
setReadListener
,在我们的简单示例中我们没有使用它。虽然我遵循了抛出运行时异常的捷径,但我应该警告您,这是一种不好的风格,因为它违反了里氏替换原则......(在这样一个简单的用例中,日志记录可以忽略不计,但仍然)
我的解决方案基于 Baeldung 的这篇文章。我发布了这个解决方案,因为这就是我最终在我的项目中使用的解决方案,本文的替代解决方案是使用
CommonsRequestLoggingFilter
:
@Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
filter.setAfterMessagePrefix("REQUEST DATA : ");
return filter;
}
我的最后建议也是
@ControllerAdvice
,但就像你当时正确地说的那样,请求已经被处理和反序列化......