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獲取更多資訊