• SpringBoot-打印请求的入参和出参


    SpringBoot-打印请求的入参和出参

    明确问题点

    • 在Controller层接受参数分为两种情况:

    情况一:接口使用 @RequestParam 接收参数
    情况二:接口使用 @RequestBody 接收参数

    • 针对情况一,代码写起来就非常简单了,我们只需要在拦截器中通过request.getParameterMap() 来获得全部 Parameter 参数就可以了

    • 但是当接口使用 @RequestBody 接收参数时,我们在拦截器中使用同样的方法获取参数,就会出现流已关闭的异常,也就导致参数读取失败了 … 这是因为 Spring 已经对 @RequestBody 提前进行处理,而 HttpServletReqeust 获取输入流时仅允许读取一次,所以会报java.io.IOException: Stream closed。

    • 解决方法就是获取HttpServletReqeust数据将它保存下来
      具体代码如下:

    
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    
    /**
     * HttpServletRequest 过滤器
     * 解决: request.getInputStream()只能读取一次的问题
     * 目标: 流可重复读
     */
    @Component
    @WebFilter(filterName = "HttpServletRequestFilter",urlPatterns = "/")
    public class HttpServletRequestFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            ServletRequest requestWrapper = null;
            if(request instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper((HttpServletRequest) request);
            }
            //获取请求中的流,将取出来的字符串,再次转换成流,然后把它放入到新 request对象中
            // 在chain.doFiler方法中传递新的request对象
            if(null == requestWrapper) {
                 chain.doFilter(request, response);
            } else {
                chain.doFilter(requestWrapper, response);
            }
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }
    
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    
    
        /***
         * HttpServletRequest 包装器
         * 解决: request.getInputStream()只能读取一次的问题
         * 目标: 流可重复读
         */
        public class RequestWrapper extends HttpServletRequestWrapper {
    
            /**
             * 请求体
             */
            private String body;
            public RequestWrapper(HttpServletRequest request) {
                super(request);
                body=getBody(request);
            }
            /**
             * 获取请求体
             * @param request 请求
             * @return 请求体
             */
            private String getBody(HttpServletRequest request) {
                return HttpContextUtils.getBodyString(request);
            }
    
            /**
             * 获取请求体
             * @return 请求体
             */
            public String getBody() {
                return body;
            }
    
    
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(getInputStream()));
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                // 创建字节数组输入流
                final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
    
                return new ServletInputStream() {
                    @Override
                    public boolean isFinished() {
                        return false;
                    }
    
                    @Override
                    public boolean isReady() {
                        return false;
                    }
    
                    @Override
                    public void setReadListener(ReadListener readListener) {
    
                    }
    
                    @Override
                    public int read() throws IOException {
                        return bais.read();
                    }
                };
            }
    
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Map;
    
    public class HttpContextUtils {
    
        /**
         * 获取query参数
         * @param request
         * @return
         */
        public static Map<String, String> getParameterMapAll(HttpServletRequest request) {
            Enumeration<String> parameters = request.getParameterNames();
    
            Map<String, String> params = new HashMap<>();
            while (parameters.hasMoreElements()) {
                String parameter = parameters.nextElement();
                String value = request.getParameter(parameter);
                params.put(parameter, value);
            }
    
            return params;
        }
    
    
        /**
         * 获取请求Body
         *
         * @param request
         * @return
         */
        public static String getBodyString(ServletRequest request) {
            StringBuilder sb = new StringBuilder();
            InputStream inputStream = null;
            BufferedReader reader = null;
            try {
                inputStream = request.getInputStream();
                reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                String line = "";
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return sb.toString();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    过滤器执行之后进入到拦截器。然后拦截器根据request类型不同,解析方式也不同

    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.nacos.common.utils.MapUtils;
    import com.alibaba.nacos.common.utils.StringUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;
    import java.util.Set;
    
    @Component
    @Slf4j
    public class LogPrintInterceptor implements HandlerInterceptor {
    
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String requestURI=request.getRequestURI();
            StringBuffer buffer=new StringBuffer();
            if(request instanceof HttpServletRequestFilter.RequestWrapper){
                buffer.append(HttpContextUtils.getBodyString(request));
            }
            if(StringUtils.isEmpty(buffer.toString())){
                Map<String, String[]> parameterMap = request.getParameterMap();
                try {
                    if (MapUtils.isNotEmpty(parameterMap)) {
                        Set<String> keySet = parameterMap.keySet();
                        for (String key : keySet) {
                            Object values = parameterMap.get(key);
                            try {
                                JSONArray jsonarray = JSON.parseArray(JSON.toJSONString(values));
                                buffer.append(key + "->" + jsonarray + "; ");
                            } catch (Exception e) {
                                buffer.append(key + "->" + values + "; ");
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("LogPrintInterceptor print error", e);
                }
            }
            log.info("LogPrintInterceptor preHandle print log,requestURI={},params=[{}]",requestURI, buffer);
            return true;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    打印返回参数采用继承ResponseBodyAdvice的方式来实现

    
    import com.alibaba.fastjson.JSON;
    import com.techwolf.samplebase.common.resp.ApiResp;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.http.server.ServletServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @ControllerAdvice
    @Slf4j
    public class InterceptResponse implements ResponseBodyAdvice<Object> {
    
    
        @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) {
            ServletServerHttpResponse responseTemp = (ServletServerHttpResponse) response;
            HttpServletResponse resp = responseTemp.getServletResponse();
            ServletServerHttpRequest sshr = (ServletServerHttpRequest) request;
            HttpServletRequest req = sshr.getServletRequest();
            String requestURI=req.getRequestURI();
            if(body instanceof ApiResp){
                ApiResp apiResp=(ApiResp) body;
                if(apiResp!=null){
                    log.info("InterceptResponse beforeBodyWrite print log,requestURI={},result={}",requestURI, JSON.toJSONString(body));
                }
            }
            return body;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在AdminMvcConfig可以配置多个拦截器

    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @EnableWebMvc
    public class AdminMvcConfig implements WebMvcConfigurer {
    
        @Autowired
        private LogPrintInterceptor logPrintInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
           registry.addInterceptor(logPrintInterceptor).addPathPatterns("/api/**");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    从校园到职场 | YK菌的2022年中总结
    [附源码]java毕业设计大学生就业信息检索系统
    docker-compose安装redis
    RPA机器人的使用条件是什么,可以使用在私域运营中吗?
    第二章 学生指导(04 小学德育 05 小学美育 06 小学生安全与心理健康教育 07 学校与家庭、社会的协调)
    NginX + PostgreSQL + uWSGI + Django
    JavaWeb总结:
    element ui框架(vuex模块化)
    Redis的BitMap使用
    [论文笔记]SiameseNet
  • 原文地址:https://blog.csdn.net/weixin_42684418/article/details/136536131