博客
关于我
Java Web 中对 ServletRequest 的一些非常规操作解决方案
阅读量:423 次
发布时间:2019-03-06

本文共 4345 字,大约阅读时间需要 14 分钟。

文章目录

1. 前言

ServletRequest 是我们搞 Java Web 经常接触的 Servlet Api 。有些时候我们要经常对其进行一些操作。这里列举一些经常的难点操作。

2. 提取 body 中的数据

前后端交互我们会在 body 中传递数据。我们如何从 body 中提取数据。通常我们会通过 IO 操作:

/**      * obtain request body      *      * @param request the ServletRequest      * @return body string   it maybe is   null      */     public static String obtainBody(ServletRequest request) {                  BufferedReader br = null;         StringBuilder sb = new StringBuilder();          try {             br = request.getReader();             String str;             while ((str = br.readLine()) != null) {                 sb.append(str);             }             br.close();         } catch (IOException e) {             log.error(" requestBody read error");         } finally {             if (null != br) {                 try {                     br.close();                 } catch (IOException e) {                     log.error(" close io error");                 }             }         }         return sb.toString();      }

看起来比较凌乱,各种异常处理,IO 开关操作,很不优雅。 如果你使用了 Java 8 你可以这样简化这种操作:

String body = request.getReader().lines().collect(Collectors.joining());

BufferedReader 提供了获取 流的方法 lines() ,我们可以通过以上方法非常方便的获取 ServletRequest 中的 body

3. ServletRequest 中的流是一次性的

不要以为上面的读取 body 操作是完美无瑕的,这里有一个坑。如果按照上面的操作 ServletRequest 中的 body 只能读取一次。 我们传输的数据都是通过流来传输的。ServletRequest 中我们实际上都是通过:

ServletInputStream inputStream = request.getInputStream()

来获取输入流,然后通过 read 系列方法来读取。Java 中的 InputStream read 方法内部有一个postion, **它的作用是标志当前流读取到的位置,每读取一次,位置就会移动一次,如果读到最后,read 方法会返回 -1,标志已经读取完了,如果想再次读取,可以调用 reset 方法,position 就会移动到上次调用 mark 的位置,mark 默认是 0,所以就能从头再读了。

能否 reset 是有条件的,它取决于 markSupported(),markSupported() 方法返回是否可以进行 mark/reset

我们再回头看 ServletInputStream ,其实现并没有重写 reset 方法并不支持 mark/reset 。所以ServletRequest 中的 IO流 只能读取一次 。

4. 可重复读取 ServletRequest 中的流

如果我们使用了个多个 Servlet Filter 进行链式调用并多次操作 ServletRequest 中的流应该怎么做? 我们可以通过 Servlet Api 提供的 javax.servlet.http.HttpServletRequestWrapper 来对其进行包装。 通过继承 HttpServletRequestWrapper :

public class ReaderRequest extends HttpServletRequestWrapper {   private String body;   public ReaderRequest(HttpServletRequest request) throws IOException {       super(request);       body = request.getReader().lines().collect(Collectors.joining());   }   @Override   public BufferedReader getReader() throws IOException {       final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());       InputStreamReader inputStreamReader = new InputStreamReader(byteArrayInputStream);       return new BufferedReader(inputStreamReader);   }}

以下是在一个 Servlet Filter 中的标准范例:

public class TestFilter extends GenericFilterBean {    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {         // 包装        ReaderRequest cachingRequestWrapper=new ReaderRequest((HttpServletRequest) servletRequest);        // 直接从包装读取        String collect = cachingRequestWrapper.getReader().lines().collect(Collectors.joining());        // 传递包装        filterChain.doFilter(cachingRequestWrapper, servletResponse);    }}

5. 如何对 ServletRequest 进行 setParameter()

从前台传入数据的时候、后台通过 HttpServletRequest 中的 getParameter(String name) 方法对数据进行获取。 如果后台想将数据放进去,下次请求或者其他请求时使用,只能通过setAttribute(String name, Object o) 放入然后从 getAttribute(String name) 获取, 无法通过 getParameter(String name) 获取。我在 就遇到了这个问题

首先说一下getParameter(String name) 是在数据从客户端到服务端之后才有效的,而 则是服务端内部的事情,只有在服务端调用了 setAttribute(String name, Object o) 之后,并且没有重定向(redirect),在没有到客户端之前 getAttribute(String name) 才有效。

如果希望在服务端中转过程中使用 setParameter() ,我们可以通过 getParameter(String name) 委托给 getAttribute(String name) 来执行。相关实现依然通过 javax.servlet.http.HttpServletRequestWrapper 来实现。

package cn.felord.spring.security.filter;    import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletRequestWrapper;    /**   * @author Felordcn   * @since 2019/10/17 22:09   */  public class ParameterRequestWrapper extends HttpServletRequestWrapper {          public ParameterRequestWrapper(HttpServletRequest request ) {          super(request);        }        @Override      public String getParameter(String name) {         return (String) super.getAttribute(name);      }  }

你也可借鉴思路实现其它你需要的功能。

6. 总结

今天我们对 ServletRequest 的一些常用的操作进行了讲解。也是我们经常在实际开发中遇到的一些问题。当然你也可以使用一些第三方包来解决这些问题。

关注公众号:Felordcn获取更多资讯

转载地址:http://vpjuz.baihongyu.com/

你可能感兴趣的文章
MySQL原理简介—8.MySQL并发事务处理
查看>>
MySQL原理简介—9.MySQL索引原理
查看>>
MySQL参数调优详解
查看>>
mysql参考触发条件_MySQL 5.0-触发器(参考)_mysql
查看>>
MySQL及navicat for mysql中文乱码
查看>>
MySqL双机热备份(二)--MysqL主-主复制实现
查看>>
MySQL各个版本区别及问题总结
查看>>
MySql各种查询
查看>>
mysql同主机下 复制一个数据库所有文件到另一个数据库
查看>>
mysql启动以后会自动关闭_驾照虽然是C1,一直是开自动挡的车,会不会以后就不会开手动了?...
查看>>
mysql启动和关闭外键约束的方法(FOREIGN_KEY_CHECKS)
查看>>
Mysql启动失败解决过程
查看>>
MySQL启动失败:Can't start server: Bind on TCP/IP port
查看>>
mysql启动报错
查看>>
mysql启动报错The server quit without updating PID file几种解决办法
查看>>
mysql命令
查看>>
mysql命令==_mysql命令
查看>>
mysql命令和mysql的配置文件
查看>>
watch
查看>>