ServletRequest
是咱们搞 Java Web
常常接触的 Servlet Api
。有些时候咱们要常常对其进行一些操做。这里列举一些常常的难点操做。html
先后端交互咱们会在 body
中传递数据。咱们如何从 body
中提取数据。一般咱们会经过 IO
操做:java
/** * 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 你能够这样简化这种操做:spring
String body = request.getReader().lines().collect(Collectors.joining());
BufferedReader
提供了获取 Java 8 Stream 流的方法 lines()
,咱们能够经过以上方法很是方便的获取 ServletRequest
中的 body
后端
不要觉得上面的读取 body
操做是完美无瑕的,这里有一个坑。若是按照上面的操做 ServletRequest
中的 body
只能读取一次。 咱们传输的数据都是经过流来传输的。ServletRequest
中咱们实际上都是经过:api
ServletInputStream inputStream = request.getInputStream()
来获取输入流,而后经过 read
系列方法来读取。Java
中的 InputStream
read
方法内部有一个postion
, **它的做用是标志当前流读取到的位置,每读取一次,位置就会移动一次,若是读到最后,read
方法会返回 -1
,标志已经读取完了,若是想再次读取,能够调用 reset
方法,position
就会移动到上次调用 mark
的位置,mark
默认是 0
,因此就能从头再读了。 可否 reset
是有条件的,它取决于 markSupported()
,markSupported()
方法返回是否能够进行 mark/reset
。app
咱们再回头看 ServletInputStream
,其实现并无重写 reset
方法并不支持 mark/reset
。因此ServletRequest
中的 IO流
只能读取一次 。ide
若是咱们使用了个多个 Servlet Filter
进行链式调用并屡次操做 ServletRequest
中的流应该怎么作? 咱们能够经过 Servlet Api
提供的 javax.servlet.http.HttpServletRequestWrapper
来对其进行包装。 经过继承 HttpServletRequestWrapper
:post
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
中的标准范例:ui
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); } }
从前台传入数据的时候、后台经过 HttpServletRequest
中的 getParameter(String name)
方法对数据进行获取。 若是后台想将数据放进去,下次请求或者其余请求时使用,只能经过setAttribute(String name, Object o)
放入而后从 getAttribute(String name)
获取, 没法经过 getParameter(String name)
获取。我在 Spring Security 实战干货: 玩转自定义登陆 就遇到了这个问题3d
首先说一下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); } }
你也可借鉴思路实现其它你须要的功能。
今天咱们对 ServletRequest
的一些经常使用的操做进行了讲解。也是咱们常常在实际开发中遇到的一些问题。固然你也可使用一些第三方包来解决这些问题。
关注公众号:Felordcn获取更多资讯