springboot 日志打印完整json格式的请求参数

经常测试人员过来告诉你接口又又又又报错了, 有时小程序或其它客户端不好抓包,这时不如自己直接在后台日志中直接打印url 和json .但是springboot 的aop无法打印出入参的json ,这时首先想到的就是filter
中打印出入参, 但是会出现流只能读一次的尴尬情况.所以需要加一个wrapper 主要的功能能是让request可重复读. 增加如下2个类就可以实现功能了

新增一个filter

package com.bit.filter;

import com.bit.filter.wrapper.BodyReaderHttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 日志记录的filter
 */
@WebFilter(urlPatterns = "/*", filterName = "logFilter")
@Slf4j
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("--------------logFilter 过滤器初始化------------");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
        printRequest(requestWrapper);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    private void printRequest(ServletRequest servletRequest) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String uri = request.getRequestURI();
        String requestBody = "";
        String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
        if (requestContentType != null) {
            //	只拦截 json 请求的参数
            if (requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE) || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)) {
                requestBody = getRequestBody(request);
                log.info("请求开始 url ==={}", uri);
                log.info("请求开始 参数 ==={}", requestBody);
            }
        }
    }

    private String getRequestBody(HttpServletRequest request) {
        int contentLength = request.getContentLength();
        if (contentLength <= 0) {
            return "";
        }
        try {
            return IOUtils.toString(request.getReader());
        } catch (IOException e) {
            log.error("获取请求体失败", e);
            return "";
        }
    }

    @Override
    public void destroy() {
        System.out.println("--------------过滤器销毁------------");
    }

}

BodyReaderHttpServletRequestWrapper

package com.bit.filter.wrapper;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

/**
 * @author mifei
 * @create 2020-06-23 13:16
 **/
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("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();
    }

    /**
     * Description: 复制输入流
     *
     * @param inputStream
     * @return
     */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

参考

https://blog.csdn.net/shangrila_kun/article/details/89308766
springMVC拦截器从Request中获取Json格式并解决request的请求流只能读取一次的问题_菜鸟sdut的博客-CSDN博客_request中的数据获取一次就没有了
https://blog.csdn.net/sdut406/article/details/81369983

解决:Spring boot系统请求/响应参数打印_代码实现_春风化雨-CSDN博客_springboot 打印请求参数
https://blog.csdn.net/jiahao1186/article/details/91870776

SpringBoot通过使用统一的数据返回格式可以提高开发效率和代码的可维护性。返回统一的JSON数据格式可以统一前后端之间的数据交互方式,减少沟通成本,并且提高了代码的可读性和可维护性。 在SpringBoot中,可以定义一个全局异常处理器(GlobalExceptionHandler),用于捕获和处理全局的异常。在异常处理器中,可以定义一个统一的数据返回格式,包括状态码、提示信息和返回数据。当发生异常时,异常处理器会将异常信息封装成统一的JSON数据格式返回给前端,以便前端进行处理。 为了实现统一的数据返回格式,可以定义一个响应对象(ResponseObject),包含对应的状态码、提示信息和返回数据。在业务逻辑处理中,统一使用ResponseObject来封装返回的数据,然后再返回给前端。 在控制器层中,可以使用ResponseEntity对象来返回统一的JSON数据格式。通过使用ResponseEntity.ok()方法可以返回一个状态码为200的成功响应,然后将ResponseObject对象作为响应的数据。这样,无论是成功还是失败,都能以统一的JSON数据格式返回给前端。 在返回数据时,可以添加一些通用的字段,如请求ID、访问时间等,以便用于日志记录和跟踪。 通过采用统一的JSON数据返回格式,可以提高代码的可读性、可维护性和规范性,方便前后端的协作和沟通。同时,也可以方便后期的维护和扩展,减少了代码的冗余和重复编写。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值