java 记录传参和返回值

本文介绍的拦截器是基于 Filter 的实现,没有使用 spring AOP 切面的技术

场景:在实际开发中,部分接口是通过 jar 包引入的,由于 Pointcut 切入的是工程包内的方法,所以引入的接口并不好覆盖。例如引入 spring security 后拥有的 /oauth/token 请求

 

传参

自定义包装类

  • 继承:HttpServletRequest 有个特性,就是 getReader() 方法获取一次之后,便无法再次获取,所以这里使用包装类,继承 HttpServletRequestWrapper
  • 构造方法:传入 HttpServletRequest ,判断是否文件流,是则不做处理
  • 获取 body:读取流 getReader(),获取 body 传参
  • 获取 params:通过自定义方法 getParams() 获取,使用了 cn.hutool.json 的 JSONObject 类
import cn.hutool.json.JSONObject;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
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;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public class RequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;
    private Map<String, String[]> paramMap;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        if (ServletFileUpload.isMultipartContent(request)) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        String body = sb.toString();
        this.body = body.getBytes(StandardCharsets.UTF_8);

        this.paramMap=request.getParameterMap();
    }

    public String getBody() {
        return new String(this.body, StandardCharsets.UTF_8);
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        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() {
                return bais.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getParams() {
        Map<String, String[]> map = this.paramMap;
        JSONObject obj = new JSONObject();
        if (Optional.ofNullable(map).isPresent()) {
            Set<String> set = map.keySet();
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                Object key = iterator.next();
                String[] arr = map.get(key);
                if (Optional.ofNullable(arr).isPresent()) {
                    for (String v : arr) {
                        obj.putOnce((String) key, v);
                    }
                }

            }
        }
        return obj.toString();
    }
}

 

返回值

自定义包装类

  • 继承:继承 HttpServletResponseWrapper
  • 构造方法:传入 HttpServletResponse
  • 获取返回值字节流:getContent()
  • 获取 params:通过自定义方法 getParams() 获取,使用了 cn.hutool.json 的 JSONObject 类
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer;

    private ServletOutputStream out;

    public ResponseWrapper(HttpServletResponse httpServletResponse) {
        super(httpServletResponse);
        buffer = new ByteArrayOutputStream();
        out = new WrapperOutputStream(buffer);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
    }

    public byte[] getContent() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    public String getResponseContent() {
        return new String(buffer.toByteArray());
    }

    class WrapperOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos;

        public WrapperOutputStream(ByteArrayOutputStream bos) {
            this.bos = bos;
        }

        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }

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

        @Override
        public void setWriteListener(WriteListener arg0) {

        }
    }

}

 

过滤器

对于一般的传入参记录,我们需要记录以下字段

  • 接口 uri
  • 接口方法 method
  • 传参 params,即问号传参
  • 传参 body,即 Content-Type = application/json

自定义过滤器,实现接口 Filter

  • urlPatterns:过滤相应规则的 url,可设置多个,传参为字符串数组
  • POINT:url 含有字符串点的不过滤,例如脚本、图片(.jsp、.jpg) 等
  • doFilter:传入 RequestWrapper 和 ResponseWrapper
  • ServletResponse:返回输出流,若不设置,返回值为空
import cn.hutool.json.JSONUtil;
import com.njc.java.property.KafkaTopicProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Slf4j
@Component
@WebFilter(filterName = "channelFilter", urlPatterns = {"/*"})
public class ChannelFilter implements Filter {

    private static final String POINT = ".";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String uri = httpRequest.getRequestURI();

        if (uri.contains(POINT)) {
            try {
                chain.doFilter(request, response);
            } catch (Exception e) {
                log.error(e.getMessage());
            }

        } else {
            try {
                RequestWrapper requestWrapper = new RequestWrapper(httpRequest);
                ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);

                String method = httpRequest.getMethod();
                String paramBody = requestWrapper.getBody();
                String param = requestWrapper.getParams();

                chain.doFilter(requestWrapper, responseWrapper);

                String msgStr = responseWrapper.getResponseContent();
                ServletOutputStream out = response.getOutputStream();
                out.write(responseWrapper.getContent());
                out.flush();

            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }
}

 

日志收集

  • 一般做法可以把日志收集到当前服务的数据库中
  • 若使用微服务架构开发,建议规划日志收集服务,通过 kafka 统一收集日志,用 MongoDB 存储日志信息
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值