0
点赞
收藏
分享

微信扫一扫

Java Web 中对 ServletRequest 的一些非常规操作解决方案


Java Web 中对 ServletRequest 的一些非常规操作解决方案_servletRequest


文章目录


  • ​​1. 前言​​
  • ​​2. 提取 body 中的数据​​
  • ​​3. ServletRequest 中的流是一次性的​​
  • ​​4. 可重复读取 ServletRequest 中的流​​
  • ​​5. 如何对 ServletRequest 进行 setParameter()​​
  • ​​6. 总结​​


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​​​ 提供了获取 ​​Java 8 Stream​​​ 流的方法 ​​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)​​​ 获取。我在 ​​Spring Security 实战干货: 玩转自定义登录​​ 就遇到了这个问题

首先说一下​​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​​ 的一些常用的操作进行了讲解。也是我们经常在实际开发中遇到的一些问题。当然你也可以使用一些第三方包来解决这些问题。

​​个人博客:https://felord.cn​​



举报

相关推荐

0 条评论