软件编程
位置:首页>> 软件编程>> java编程>> Springboot如何设置过滤器及重复读取request里的body

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

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

标签:Springboot,过滤器,request,body

需求:

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

0
投稿

猜你喜欢

  • IEnumerable这个接口在MSDN上是这么说的,它是一个公开枚举数,该枚举数支持在非泛型集合上进行简单的迭代。换句话说,对于所有数组的
  • 一、解决的痛点      1、免搭建后端开发环境。   &n
  • 1.查询后获取对应的数据集后,传递参数strcodeName,根据数据集中strcodeName的匹配对应字段,获取数据集中对应的目的字段p
  • 三态的基本概念1, 临时状态(Transient):也叫自由态,只存在于内存中,而在数据库中没有相应数据。用new创建的对象,它没有持久化,
  • 本文实例讲述了Java使用组合模式实现表示公司组织结构功能。分享给大家供大家参考,具体如下:一、模式定义组合模式:将对象组合成树形结构以表示
  • 利用javax.swing.Timer类设计并实现一个模拟秒表功能的应用程序。程序中显示不断递增的时间,同时包含允许用户启动和终止计时功能的
  • 在平常的开发过程中,我们的ListView可能不只是简单的显示下文本或者按钮,更多的是显示复杂的布局,这样的话,我们就得自己写布局和自定义a
  • Java IO中File的使用是比较频繁的,在文件的上传和删除中都会用到的。比如我们在写管理系统的时候有可能会用到图片的上传,和删除。那么我
  • 概述:App几乎都离不开与服务器的交互,本文主要讲解了flutter网络请求三种方式 flutter自带的HttpClient、 第三方库h
  • 现在很多app的支付、输入密码功能,都已经开始使用自定义数字键盘,不仅更加方便、其效果着实精致。下面带着大家学习下,如何 * 微信的数字键盘,
  • 本文实例为大家分享了Java实现打字游戏的具体代码,供大家参考,具体内容如下新建一个项目,然后在src里面建一个MyGame.java文件,
  • Java反射详解本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,
  • 2015年Google IO大会分布了DataBinding库,能够更快捷便利的实现MVVM结构模式。但是,通过对DataBinding的学
  • 本文实例为大家分享了java实现页显示效果的具体代码,供大家参考,具体内容如下效果图如下:实现步骤:1.创建实体User.class,参考代
  • 改了个bug,发现这个东西以前不知道,搜索了一下,看到的都是长篇大论,还谈js的源码,也是醉了。我就简单的说说这个是干啥的。简单说:就是触发
  • 本文以实例形式讲述了C#泛型的用法,有助于读者深入理解C#泛型的原理,具体分析如下:首先需要明白什么时候使用泛型:当针对不同的数据类型,采用
  • 一、前言Hello,又见面了,今天分享如何使用Unity制作计算器,难度中等,可以用来学习,或者当成其他项目的小组件导入。当然,也可以导出来
  • 概述JavaScript是目前web开发中不可缺少的脚本语言,js不需要编译即可运行,运行在客户端,需要通过浏览器来解析执行JavaScri
  • springmvc dao层和service层的区别首先解释面上意思,service是业务层,dao是数据访问层这个问题我曾经也有过,记得以
  • 做Android开发的程序员必须知道android客户端应该如何与服务端进行交互,这里主要介绍的是使用json数据进行交互。服务端从数据库查
手机版 软件编程 asp之家 www.aspxhome.com