当前位置: 代码网 > it编程>编程语言>Java > SpringBoot自定义过滤器获取HttpServletRequest和HttpServletResponse的参数

SpringBoot自定义过滤器获取HttpServletRequest和HttpServletResponse的参数

2024年08月03日 Java 我要评论
公司的老系统改造:由于接口日志不全,接口太多,也无法每个接口都加上日志,所以要在网关层统一记录一下日志,并存到数据库中,(以后计划要存储到ES中)

1、 需求:

公司的老系统改造:由于接口日志不全,接口太多,也无法每个接口都加上日志,所以要在网关层统一记录一下日志,并存到数据库中,(以后计划要存储到es中)

2、了解过滤器:

过滤器是基于servlet规范的组件,作用于整个请求和响应过程,无法直接访问spring mvc的上下文。过滤器先于拦截器执行,过滤器的执行顺序由注册顺序决定,拦截器的执行顺序由配置的顺序决定。
实现方式

  • 过滤器是基于servlet规范的一部分,需要实现javax.servlet.filter接口来创建自定义过滤器。javax.servlet.filter接口定义了以下三个方法:
    • void init(filterconfig config) throws servletexception:
      该方法在过滤器初始化时被调用,用于进行初始化操作。
      参数config包含了过滤器的配置信息,可以通过该对象获取配置参数。
    • void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception:
      该方法在每次请求被过滤时被调用,用于对请求和响应进行过滤和处理。
      参数request表示当前的请求对象,参数response表示当前的响应对象,参数chain表示过滤器链。
      在该方法中可以对请求和响应进行修改或包装,并通过调用chain.dofilter(request, response)方法将请求传递给下一个过滤器或目标资源。
    • void destroy():
      该方法在过滤器被销毁时被调用,用于进行清理操作。
      拦截器和过滤器的区别可以参考:
      https://blog.csdn.net/xiaoweiwei11/article/details/130860352

3、开发难点

3.1 httpservletrequest 的post请求方式,获取不到入参

3.1.1 原因:

获取json入参的时候,一般都是用流的方式获取,但是会引发另外一个问题:过滤器相关逻辑走完之后,接口层的@requestbody修饰的对象参数都会失效,并且调试之后释放会报错

org.springframework.http.converter.httpmessagenotreadableexception: i/o error while reading input message; nested exception is java.io.ioexception: stream closed

因为用了流,并且存在流关闭,一次请求流关闭了就只能读取一次,所以到接口层就会报错

3.1.2 解决思路:

不管流关没关闭,要把流中的参数,延伸到后面的接口去用就可以了

3.1.3 代码实现

需要

  • 过滤器:httpservletrequestfilter
  • 过滤器:accesslogfilter 调用接口记录日志
  • 防流丢失类:myrequestwrapper (防止流读取完之后就丢失了)
  • 过滤器bean(在springboot的启动类中注入过滤器):filterregistrationbean
  • 获取参数的类:httprequesthelper
/**
 * 获取参数的类
 * @description: 获取流并将想要的结果通过拼接返回
 **/
public class httprequesthelper {
    public static string getbodystring(httpservletrequest request) throws ioexception {
        stringbuilder sb = new stringbuilder();
        inputstream inputstream = null;
        bufferedreader reader = null;
        try {
            inputstream = 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: 防止流丢失
 **/
public class myrequestwrapper extends httpservletrequestwrapper {
    private final byte[] body;
 
    public myrequestwrapper(httpservletrequest request) throws ioexception {
        super(request);
        //返回参数字节数组
        body = httprequesthelper.getbodystring(request).getbytes(charset.forname("utf-8"));
    }
 
    @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) {
 
            }
        };
    }
 
}
/**
 * @description: 使用过滤器处理流,将当前流放到一个新的request对象中
 **/
public class httpservletrequestfilter implements filter {
    @override
    public void destroy() {
 
    }
 
    @override
    public void dofilter(servletrequest request, servletresponse response,
                         filterchain chain) throws ioexception, servletexception {
        servletrequest requestwrapper = null;
        if (request instanceof httpservletrequest) {
            requestwrapper = new myrequestwrapper((httpservletrequest) request);
        }
        //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
        // 在chain.dofiler方法中传递新的request对象
        if (requestwrapper == null) {
            chain.dofilter(request, response);
        } else {
            chain.dofilter(requestwrapper, response);
        }
    }
 
    @override
    public void init(filterconfig arg0) throws servletexception {
 
    }
}
@slf4j
@component
public class accesslogfilter extends onceperrequestfilter {

    @value("${spring.profiles.active}")
    private string springprofile;

    @value("${log-application.ignore-uri}")
    private string ignoreuris;

    @reference(check = false)
    private weboperatelogservice weboperatelogservice;

    @override
    protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain)
            throws servletexception, ioexception {
            // 重点:myrequestwrapper 
        myrequestwrapper req = new myrequestwrapper(request);
        // 解决:获取不到httpservletresponse响应信息
        contentcachingresponsewrapper resp = new contentcachingresponsewrapper(response);
        try {
            // execution request chain
            filterchain.dofilter(req, resp);
            //uri白名单
            boolean haveignoreuri = arrays.stream(ignoreuris.split(",")).anymatch(ignoreuri -> request.getrequesturi().contains(ignoreuri));
            if (!haveignoreuri) {
                savewebrequestlog(request, response, resp);
            }
        } catch (exception e) {
            log.error("记录web接口日志异常!原因:" + e);
        } finally {
            // finally remember to respond to the client with the cached data.
            resp.copybodytoresponse();
        }
    }

    private void savewebrequestlog(httpservletrequest request, httpservletresponse response, contentcachingresponsewrapper resp) throws exception {
        weboperatelog weboperatelog = new weboperatelog();
        string method = request.getmethod();
        weboperatelog.setmethod(request.getmethod());
        weboperatelog.setrequesturl(request.getrequesturi());
        string inputparams = stringsymbolenum.empty.getcode();
        if (method.equals("get")) {
            inputparams = request.getquerystring();
        } else {
        // 重点
            inputparams = httprequesthelper.getbodystring(request);
        }
        weboperatelog.setinputparams(inputparams);
        weboperatelog.setservletpath(request.getservletpath());
        weboperatelog.setremoteaddr(request.getremoteaddr());
        weboperatelog.setremotehost(request.getremotehost());
        weboperatelog.setremoteport(stringsymbolenum.empty.getcode() + request.getremoteport());
        weboperatelog.setremoteaddr(request.getlocaladdr());
        weboperatelog.setlocaladdr(request.getlocalname());
        weboperatelog.setlocalport(stringsymbolenum.empty.getcode() + request.getlocalport());
        weboperatelog.setservername(request.getservername());
        weboperatelog.setserverport(stringsymbolenum.empty.getcode() + request.getserverport());
        weboperatelog.setrequesturl(request.getrequesturl().tostring());
        string outputparams = stringsymbolenum.empty.getcode();
        if (!objectutils.isempty(response)) {
            weboperatelog.setstatus(stringsymbolenum.empty.getcode() + response.getstatus());
            // 重点
            byte[] responsebody = resp.getcontentasbytearray();
            if (responsebody != null && responsebody.length > 0) {
                outputparams = new string(responsebody, standardcharsets.utf_8);
                weboperatelog.setoutputparams(outputparams);
            }
        }
        weboperatelog.setcreateddate(new date());
        weboperatelog.setprofile(springprofile);
        weboperatelogservice.save(weboperatelog);
    }
}
    @bean
    public filterregistrationbean httpservletrequestreplacedregistration() {
        filterregistrationbean registration = new filterregistrationbean();
        registration.setfilter(new httpservletrequestfilter());
        registration.addurlpatterns("/*");
        registration.addinitparameter("paramname", "paramvalue");
        registration.setname("httpservletrequestfilter");
        registration.setorder(1);
        return registration;
    }

3.2 httpservletresponse: 获取不到响应信息

3.3 了解onceperrequestfilter

spring的onceperrequestfilter类实际上是一个实现了filter接口的抽象类。

/**
 * same contract as for {@code dofilter}, but guaranteed to be
 * just invoked once per request within a single request thread.
 * see {@link #shouldnotfilterasyncdispatch()} for details.
 * <p>provides httpservletrequest and httpservletresponse arguments instead of the
 * default servletrequest and servletresponse ones.
 */
protected abstract void dofilterinternal(
	httpservletrequest request, httpservletresponse response, filterchain filterchain)
	throws servletexception, ioexception;
#通过上面的该方法的注释说明,可以发现onceperrequestfilter过滤器保证一次请求只调用一次dofilterinternal方法;如内部的forward不会再多执行一次
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com