После многих попыток я здесь, чтобы спросить, есть ли способ зарегистрировать входной запрос JSON перед его десериализацией в объект Java.
Мои попытки:
1: Я могу правильно зарегистрировать запрос перед десериализацией, но затем выдает «Ошибка ввода-вывода при чтении входного сообщения».
Код: RequestInterceptor (getRequestBody из HttpServletRequest)
@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: ошибка ввода-вывода при чтении входного сообщения
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, и, более того, 2 идентификатора из MappedDiagnosticContext (| 99d30bc4-4102-482a-a78e-614f2c0f5876 | 31b3b078-737f-4c60-a429-0596a0736b7a |
), записанные перехватчиками, не регистрируются. .
Код: 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
поскольку входной поток можно использовать только один раз.
Этот фильтр сохраняется в нашем собственном CachedBodyHttpServletRequest
, чтобы иметь возможность использовать его как для регистрации, так и для дальнейшей обработки.
@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 в массив байтов и позволяет вам получать InputStream несколько раз, возвращая реализацию ServletInputStream нашего CachedBodyServletInputStream.
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
(не работает надежно для весенней загрузки < 2.0):
@Bean
CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
filter.setAfterMessagePrefix("REQUEST DATA : ");
return filter;
}
Не забудьте также настроить уровень ведения журнала DEBUG:
logging:
level:
org.springframework.web.filter: DEBUG
Обратите внимание, что запрос регистрируется в конце процесса API.
Моим последним предложением также было бы @ControllerAdvice
, но, как вы правильно сказали, запрос уже обработан и десериализован...