Java ServletRequest 的一些實戰技巧分享

1. 前言

ServletRequest 是咱們搞 Java Web 常常接觸的 Servlet Api 。有些時候咱們要常常對其進行一些操做。這裏列舉一些常常的難點操做。java

2. 提取 body 中的數據

先後端交互咱們會在 body 中傳遞數據。咱們如何從 body 中提取數據。一般咱們會經過 IO 操做:spring

/**
   * 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 中的 bodyapp

3. ServletRequest 中的流是一次性的

不要覺得上面的讀取 body 操做是完美無瑕的,這裏有一個坑。若是按照上面的操做 ServletRequest 中的 body 只能讀取一次。 咱們傳輸的數據都是經過流來傳輸的。ServletRequest 中咱們實際上都是經過:ide

ServletInputStream inputStream = request.getInputStream() 來獲取輸入流,而後經過 read 系列方法來讀取。Java 中的 InputStream read 方法內部有一個postion, **它的做用是標誌當前流讀取到的位置,每讀取一次,位置就會移動一次,若是讀到最後,read 方法會返回 -1,標誌已經讀取完了,若是想再次讀取,能夠調用 reset 方法,position 就會移動到上次調用 mark 的位置,mark 默認是 0,因此就能從頭再讀了。 可否 reset 是有條件的,它取決於 markSupported(),markSupported() 方法返回是否能夠進行 mark/reset 。post

咱們再回頭看 ServletInputStream ,其實現並無重寫 reset 方法並不支持 mark/reset 。因此ServletRequest 中的 IO流 只能讀取一次 。ui

4. 可重複讀取 ServletRequest 中的流

若是咱們使用了個多個 Servlet Filter 進行鏈式調用並屢次操做 ServletRequest 中的流應該怎麼作? 咱們能夠經過 Servlet Api 提供的 javax.servlet.http.HttpServletRequestWrapper 來對其進行包裝。 經過繼承 HttpServletRequestWrapper :spa

public class ReaderRequest extends HttpServletRequestWrapper { private String body;code

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 的一些經常使用的操做進行了講解。也是咱們常常在實際開發中遇到的一些問題。固然你也可使用一些第三方包來解決這些問題。

原創做者:碼農小胖哥 轉載地址:gper.club/articles/7e…

相關文章
相關標籤/搜索