Springboot如何设置过滤器及重复读取request里的body

作者:蟑螂恶霸qaq 时间:2022-05-26 04:59:20 

需求:

request的content-type为applciation/json,进入controller之前需要把body中的参数取出来做一次处理,然后和hearder中的另一个参数做对比。

思路:

加一个过滤器,在过滤器中取出参数做处理,然后比较

注意:

body里的数据用流来读取,只能读取一次

HttpServletRequest的输入流只能读取一次的原因

我们先来看看为什么HttpServletRequest的输入流只能读一次,当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。

InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。

InputStream默认不实现reset(),并且markSupported()默认也是返回false,这一点查看其源码便知:

Springboot如何设置过滤器及重复读取request里的body

我们再来看看ServletInputStream,可以看到该类没有重写mark(),reset()以及markSupported()方法:

Springboot如何设置过滤器及重复读取request里的body

综上,InputStream默认不实现reset的相关方法,而ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。

重复读取body中数据的方法

这个自定义的requestWrapper继承了HttpServletRequestWrapper ,HttpServletRequestWrapper 是一个ServletRequest的包装类同时也是ServletRequest的实现类。

在这个自定义的requestWrapper里,用一个String做缓存,在构造方法里先把request的body中的数据缓存起来,然后重写了getInputStream,返回这个缓存的body,而不是从流中读取。

这样,在需要多次读取body的地方,只需要在过滤器中把原来的request换成这个自定义的request,然后把这个自定义的带缓存功能的request传到后续的过滤器链中。

public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {
   private final String body;

/**
    *
    * @param request
    */
   public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException{
       super(request);
       StringBuilder sb = new StringBuilder();
       InputStream ins = request.getInputStream();
       BufferedReader isr = null;
       try{
           if(ins != null){
               isr = new BufferedReader(new InputStreamReader(ins));
               char[] charBuffer = new char[128];
               int readCount = 0;
               while((readCount = isr.read(charBuffer)) != -1){
                   sb.append(charBuffer,0,readCount);
               }
           }else{
               sb.append("");
           }
       }catch (IOException e){
           throw e;
       }finally {
           if(isr != null) {
               isr.close();
           }
       }

sb.toString();
       body = sb.toString();
   }

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

@Override
   public ServletInputStream getInputStream() throws IOException {
       final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
       ServletInputStream servletIns = new ServletInputStream() {
           @Override
           public boolean isFinished() {
               return false;
           }

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

@Override
           public void setReadListener(ReadListener readListener) {

}

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

springboot的过滤器

2个注解:

  • @WebFilter(过滤器上)

  • @ServletComponentScan (加在启动类上,支持servlet components扫描(为了webfilter))

@Order(999) // 序号越低,优先级越高
// 加上WebFilter即可成为过滤器
@WebFilter(filterName="myFilter", urlPatterns="/api/workorder/service/selfAppeal")
public class ExternalFilter implements Filter  {

private final static Logger logger = LoggerFactory.getLogger(ExternalFilter.class);    
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
       logger.info("filter init");
   }

@Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
           throws IOException, ServletException {
       ResponseObject object = new ResponseObject();
       HttpServletRequest req = (HttpServletRequest)servletRequest;
       HttpServletResponse res = (HttpServletResponse)servletResponse;
       // 一个request的包装类,初始化时缓存了body,重写了getInputStream返回缓存的body,实现重复读取body
       BodyReaderRequestWrapper requestWrapper  = new BodyReaderRequestWrapper(req);
       String requestURI = requestWrapper.getRequestURI();
       System.out.println("--------------------->过滤器:请求地址" + requestURI);
       String md5 = requestWrapper.getHeader("md5")  ;

if (md5 == null || !md5.toLowerCase().equals(MD5.md5(ReqGetBody.getBody(requestWrapper)).toLowerCase())) {
           object.setStatus(501);
           object.setStatusText("数据md5校验失败");
           render(object, res);
           return;
       }
       // 这里传递下去的就是自定义的request了,所以后续的Controller才能重复读取到body里的参数
       filterChain.doFilter(requestWrapper, res);
   }

@Override
   public void destroy() {
   }

/**
   * @Title: render
   * @Description: 发送Response
   * @param object
   * @param response void
   * @author MasterYi
   * @date 2020年1月15日上午10:48:45
   */
   private void render(ResponseObject object, HttpServletResponse response) {
       response.setContentType("application/json;charset=UTF-8");
       try {
           response.setStatus(200);
           response.getWriter().write(JSONObject.toJSON(object).toString());
       } catch (IOException e) {
           logger.error("ExternalFilter写入response异常");
       }
   }
}

上面的getBody的代码

从body中取值的具体操作

public class ReqGetBody {
   static public String getBody(HttpServletRequest request) {
       try {
           ServletInputStream in = request.getInputStream();
           String body;
           body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));

return body;
       } catch (IOException e) {
           e.printStackTrace();
           return "";
       }
   }
}

来源:https://blog.csdn.net/qq_34548229/article/details/104014374

标签:Springboot,过滤器,request,body
0
投稿

猜你喜欢

  • android Retrofit2网络请求封装介绍

    2022-02-09 18:46:49
  • 一篇超详细的Spring Boot整合Mybatis文章

    2022-01-27 10:02:58
  • 浅谈virtual、abstract方法和静态方法、静态变量理解

    2022-08-29 02:29:14
  • Android开发中TextView 实现右上角跟随文本动态追加圆形红点

    2022-09-07 07:20:57
  • java中最易犯错的特殊字符示例详解

    2022-09-27 17:51:08
  • ssm mybatis如何配置多个mapper目录

    2021-12-06 14:08:25
  • Android WebView 常见问题及处理方案

    2023-04-13 04:11:57
  • C# 重写ComboBox实现下拉任意组件的方法

    2022-01-24 03:07:29
  • Spring+MyBatis实现数据读写分离的实例代码

    2021-08-31 04:34:48
  • 自定义Android圆形进度条(附源码)

    2023-09-09 22:54:57
  • android照相、相册获取图片剪裁报错的解决方法

    2021-11-21 20:18:14
  • Java定义泛型方法实例分析

    2023-11-20 20:28:22
  • java8 集合之Stack详解及实例

    2023-08-02 16:04:07
  • Java实现人脸识别登录、注册等功能(最新完整版)

    2022-09-25 00:20:13
  • spring mvc中的@ModelAttribute注解示例介绍

    2023-10-15 07:07:06
  • 详解C# Lazy Loading(延迟加载)

    2021-09-02 03:02:58
  • Intellij IDEA根据maven依赖名查找它是哪个pom.xml引入的(图文详解)

    2023-07-20 07:49:35
  • IDEA中JetBrains Mono字体的正确安装姿势

    2022-03-12 12:21:40
  • JVM 运行时数据区与JMM 内存模型

    2022-08-12 10:49:27
  • C#中流的使用和分类

    2022-10-04 22:17:41
  • asp之家 软件编程 m.aspxhome.com