interface org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice
I hope to add some specific attribute values to HttpServletRequest
in the method hook of RequestBodyAdvice#afterBodyRead
. Can Spring add HttpServletRequest
to the method parameters
example:
public interface RequestBodyAdvice {
boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType, HttpServletRequest req);
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType, HttpServletRequest req) throws IOException;
Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType, HttpServletRequest req);
@Nullable
Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType, HttpServletRequest req);
}
Comment From: rstoyanchev
HttpInputMessage
is a common contract that's used by client and server code, for message conversion, in the RestTemplate
, and so on, and needs to remain so. All methods of RequestBodyAdvice
though do pass the HttpServletRequest
so you can use that.
Comment From: brucelwl
@rstoyanchev All the methods of RequestBodyAdvice
did not pass the HttpServletRequest
, I cannot obtain HttpServletRequest
from RequestBodyAdvice
, please see source code: https://github.com/spring-projects/spring-framework/blob/0d5a7db2388dd580b5d22f4fa7a0894a51c630ad/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestBodyAdvice.java#L40
Comment From: rstoyanchev
Right, apologies. I got mixed up with your snippet above which is the suggestion.
Generally it's better to describe the problem rather than the solution. There may be other options too. In any case, changing a contract like that is not something we can do easily. It will have impact on others.
You can downcast HttpInputMessage to ServletServerHttpRequest and get the servlet request from that.
Comment From: brucelwl
@rstoyanchev Unfortunately, HttpInputMessage
cannot be converted to ServletServerHttpRequest
Comment From: brucelwl
@rstoyanchev This issue has not been resolved, closing it is not appropriate
Comment From: rstoyanchev
@brucelwl you are asking for a contract change that would break countless applications, and you have not even explained why you're asking for that and what you're aiming to achieve.
Comment From: brucelwl
@rstoyanchev Thank you for your reply, I hope to add some specific attribute values to HttpServletRequest
in the method implementation of RequestBodyAdvice#afterBodyRead
, But I am unable to obtain the HttpServletRequest object
Comment From: rstoyanchev
That's very little information that doesn't allow me to provide any further advice or comment.
Comment From: brucelwl
@rstoyanchev Thank you for your reply
I want to print the @RequestBody
request parameters and Response data in the log, so I need to record the RequestBody
in the HttpServletRequest
at the beginning of the request and print the log together after the response. The example code is as follows:
But in order to obtain HttpServletRequest
in the afterBodyRead
method, I must use ThreadLocal
to bind the current request's HttpServletRequest
. I believe this is not the most ideal solution, CanHttpServletRequest
be directly passed in method parameters or in HttpInputMessage
?
@Slf4j
@ControllerAdvice
public class RequestResponseBodyAdvice extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
HttpServletRequest request = ThreadBindRequestContainer.getServletRequest();
Method method = parameter.getMethod();
String declaringClass = method.getDeclaringClass().getSimpleName();
String handlerMethod = method.toString().substring(method.toString().indexOf(declaringClass));
request.setAttribute("handlerMethod", handlerMethod);
request.setAttribute("req_body", body);
return body;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
servletRequest.setAttribute("resp_body", body);
return body;
}
}
//@Component
public class RequestResponseBodyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ThreadBindRequestContainer.bind((HttpServletRequest) servletRequest);
try {
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception ex) {
log.error("", ex);
} finally {
ThreadBindRequestContainer.remove();
}
log.info("url:{}, Handler method:{}, arg:{}, Response:{}", requestURI, handlerMethod, req_body, resp_body);
}
}
public class ThreadBindRequestContainer {
private static final ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<>();
public static void bind(HttpServletRequest request) {
threadLocal.set(request);
}
public static void remove() {
threadLocal.remove();
}
public static HttpServletRequest getServletRequest() {
return threadLocal.get();
}
}