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();
    }

}