統一日誌輸出打印POST請求參數

衆所周知,request.getInputStream()只能調一次。如果希望在請求進入Controller之前統一打印請求參數(攔截器或過濾器),又不影響業務,我們只能將獲取到的輸入流緩存起來,後續都從緩存中獲取即可。

首先,自定義一個ServletInputStream

package com.cjs.example.log.filter;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * @Author: ChengJianSheng
 * @Date: 2023/3/6
 */
public class CustomServletInputStream extends ServletInputStream {

    private ByteArrayInputStream inputStream;

    public CustomServletInputStream(byte[] body) {
        this.inputStream = new ByteArrayInputStream(body);
    }

    @Override
    public boolean isFinished() {
        return inputStream.available() == 0;
    }

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

    @Override
    public void setReadListener(ReadListener readListener) {

    }

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

然後,自定義一個HttpServletRequestWrapper

package com.cjs.example.log.filter;

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

/**
 * @Author: ChengJianSheng
 * @Date: 2023/3/6
 */
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CustomServletInputStream(body);
    }

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

接下來,寫一個過濾器,在過濾器中打印請求參數

package com.cjs.example.log.filter;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * @Author: ChengJianSheng
 * @Date: 2023/3/6
 */
public class LogFilter implements Filter {

    private final static Logger logger = LoggerFactory.getLogger(LogFilter.class);

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        printLog(requestWrapper);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    private void printLog(CustomHttpServletRequestWrapper requestWrapper) throws IOException {
        logger.info("請求URL: {}", requestWrapper.getRequestURL());
        String method = requestWrapper.getMethod();
        if ("GET".equalsIgnoreCase(method)) {
            logger.info("請求參數: {}", requestWrapper.getQueryString());
        } else if ("POST".equalsIgnoreCase(method) && "application/json".equalsIgnoreCase(requestWrapper.getContentType())) {
            String body = IOUtils.toString(requestWrapper.getInputStream(), requestWrapper.getCharacterEncoding());
            logger.info("請求參數: {}", body);
        }
    }
}

請求經過過濾器的時候,首先在構造CustomHttpServletRequestWrapper的時候將請求中的InputStream轉成字節數字緩存到內存中,然後後面每次再getInputStream()的時候都從緩存中取出內容並返回一個新的ServletInputStream

最後,定義一個配置類

package com.cjs.example.log.config;

import com.cjs.example.log.filter.LogFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: ChengJianSheng
 * @Date: 2023/3/6
 */
@Configuration
public class CustomLogAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public LogFilter logFilter() {
        return new LogFilter();
    }
}

在resources/META-INF/spring.factories中新增一行自動配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.cjs.example.log.config.CustomLogAutoConfiguration

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章