Spring包装请求

Java 工具 —— 包装Servlet

标签(空格分隔): java 工具类 servlet


前言:在日常的接口开发中,经常需要对接口参数生成签名,以验证请求是否被修改。但目前通用的请求格式都以json数据作为参数,这种需要获取流对象来读取数据,这样,如果在拦截器中进行读取验证,在之后Controller中的参数封装就会报错,因为流数据只能读取一次。

request的请求参数获取方式

一、只适用于GET

getQueryString()

只适用于GET,比如客户端发送http://localhost/testServlet?a=b&c=d&e=f,通过request.getQueryString()得到的是a=b&c=d&e=f

二、共用方法

getParameter()

GET和POST都可以用。request的getparameter方法的作用是获取到客户端通过表单或url请求参数所发送过来的参数值。application/x-www-form-urlencoded对于这种数据格式是有效的,对于json或是application/form-data都是获取不到数据的。

getInputStream()、getReader()

针对上面情况,在getParameter()获取不到数据时,我们可以调用流对象来获取body数据。

包装ServletRequest以多次读取数据

由于流对象只能读取一次,这样,如果在拦截器中进行读取验证,在之后Controller中的参数封装就会报错。我们可以在第一次读取流对象内容时将其存在一个变量中,以实现多次读取。

  • 首先创建包装类
/**
 * 请求体参数包装类,用于解决二次读取body数据问题
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /**用于存储body内容的变量 */
    private byte[] requestBody = null;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //将原requestbody数据copy到requestBody中
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        //模拟构建一个流对象,用于每次的数据读取,每次都会创建一个新的
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);

        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) {

            }
        };
    }

    //用新创建的字节流对象来构建字符流对象
    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}
  • 其次,我们需要将原有的request替换为我们创建的包装类,这会用到Filter过滤器,,在过滤器中完成对象的替代工作
@WebFilter(filterName = "bodyReaderFilter", urlPatterns = "/*")
public class BodyReaderFilter implements Filter {

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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("进入过滤器,替换request对象");
        ServletRequest requestWrapper = null;
        if (servletRequest instanceof HttpServletRequest){
            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest)servletRequest);
        }
        if (null == requestWrapper){
            filterChain.doFilter(servletRequest,servletResponse);
        }else {
            filterChain.doFilter(requestWrapper,servletResponse);
        }
    }
}
  • 最后,需要在bean容器里去注册Filter过滤器实例
@Bean
    public FilterRegistrationBean<BodyReaderFilter> Filters() {
        FilterRegistrationBean<BodyReaderFilter> registrationBean = new FilterRegistrationBean<BodyReaderFilter>();
        registrationBean.setFilter(new BodyReaderFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("tokenFilter");
        return registrationBean;
    }

Filter与Interceptor的区别

  • 规范不同:Filter是Servlet规范规定的,依赖于Servlet容器;Interceptor是Spring规定的,依赖于Spring框架。
  • 实现原理不同:Filter基于函数回调;Interceptor基于java反射。
  • 使用范围不同:Filter依赖于Servlet容器,只能用于Web程序;Interceptor既可用于Web程序,也可以用于Application、Swing等程序中。
  • 使用的资源不同:Interceptor是Spring的一个组件,归Spring管理,配置在Spring文件中,可以使用Spring里的任何资源和对象,例如Service对象、数据源、事务管理等,通过IoC注入到Interceptor既可;Filter则不能。
  • 深度不同:Filter只在Servlet前后起作用,Interceptor能够深入到方法前后,异常抛出前后等。在Spring中应该优先使用Interceptor。

Filter的运行时间是在Interceptor之前。

Interceptor中有三个方法,preHandler(), postHandle(), afterCompletion()这三个方法分别在进入DispatcherServlet之前,在Controller执行之后,在页面渲染完成返回给客户端之前执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值