Java Web 中對 ServletRequest 的一些很是規操做解決方案

servletrequest.png

1. 前言

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

2. 提取 body 中的數據

先後端交互咱們會在 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後端

3. ServletRequest 中的流是一次性的

不要覺得上面的讀取 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/resetapp

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

4. 可重複讀取 ServletRequest 中的流

若是咱們使用了個多個 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);
    }
}

5. 如何對 ServletRequest 進行 setParameter()

從前臺傳入數據的時候、後臺經過 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);
      }
  }

你也可借鑑思路實現其它你須要的功能。

6. 總結

今天咱們對 ServletRequest 的一些經常使用的操做進行了講解。也是咱們常常在實際開發中遇到的一些問題。固然你也可使用一些第三方包來解決這些問題。

關注公衆號:Felordcn獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索