摘要:拓展閱讀調用鏈系列解讀中的貪吃蛇調用鏈系列輕調用鏈實現在中,協議的請求響應模型是由規范容器如實現的。在這篇文章中,我會向大家具體介紹如何從零開始捕獲和。配置以后,我們就可以從的方法中獲取到和后文簡稱和了。三獲取和獲取的方式大體相同。
拓展閱讀:調用鏈系列(1):解讀UAVStack中的貪吃蛇
調用鏈系列(2):輕調用鏈實現
在Java中,HTTP協議的請求/響應模型是由Servlet規范+Servlet容器(如Tomcat)實現的。換句話說,在類Tomcat容器中,一次完整的HTTP請求都是通過實現Servlet規范完成的;Spring、Jesery 等技術棧也是在Servlet規范基礎上封裝的。因此我們可以借助底層的Servlet規范來獲取Java技術棧中HTTP的body和header,即通過攔截用戶自定義實現的HttpServlet類中的HttpServletRequest和HttpServletResponse,獲取HTTP的body和header。
通過閱讀前幾篇文章大家知道,調用鏈模型和架構都是依托UAVStack的中間件增強框架技術實現的。在這篇文章中,我會向大家具體介紹如何從零開始捕獲body和header。
一、攔截http請求想要在盡可能少改動代碼的前提下從請求中提取body和header,必須對進入容器的請求進行統一攔截,否則就需要在所有HttpServlet實現類中嵌入代碼。這里要再次感謝Servlet規范制定者為我們提供的filter機制。
根據Servlet規范,filter是一個可重用的代碼段,可以轉換HTTP requests、responses和header信息的內容。過濾器一般不會為一個request創建一個響應,而是會修改或適配一個request和response。filter主要提供四種攔截方式:
REQUEST:直接訪問目標資源時執行過濾器。包括:在地址欄中直接訪問、表單提交、超鏈接、重定向,只要在地址欄中可以看到目標資源的路徑,就是REQUEST;
FORWARD:轉發訪問執行過濾器。包括RequestDispatcher#forward()方法、< jsp:forward>標簽都是轉發訪問;
INCLUDE:包含訪問執行過濾器。包括RequestDispatcher#include()方法、< jsp:include>標簽都是包含訪問;
ERROR:當目標資源在web.xml中配置為< error-page>中時,并且真的出現了異常,轉發到目標資源時,會執行過濾器。
這里我們只需使用REQUEST模式。配置filter以后,我們就可以從filter的doFilter方法中獲取到HttpServletRequest和HttpServletResponse(后文簡稱request和response)了。
二、獲取header上文中我們已經通過filter機制獲取了request和response。打開對應源碼實現我們可以發現如下API:
規范中已經為我們提供API直接獲取header,通過組合使用getHeaderNames()和getHeader(String name)方法我們可以輕松獲取到request和response中的header。
三、獲取bodyrequest和response獲取body的方式大體相同。此處我們先以request為例,后文會對不同之處進行適配。
從request的API中可以發現,body在Java中是以ServletInputStream形式存儲的,并且ServletInputStream是繼承的InputStream。若直接讀取,用戶獲取到的body將為空(因為InputStream只能被讀取一次,除非把指針回執)。這里我們就需要借助Servlet的wrapper機制了。
四、Servlet中的wrapper這里簡單介紹一下requestWrapper和responseWrapper。wrapper是一種裝飾模式,在Servlet規范中通過繼承HttpServletResponseWrapper和HttpServletRequestWrapper實現,相當于為request和response進行了一次套殼,類似于Java中的代理,這樣所有操作request和response的動作都會經過我們的自定義wrapper,使重復獲取request和response中的body成為可能。
五、編寫自己的wrapper我們以request為例,解釋如何編寫自定義wrapper。打開servlet-api源碼可見HttpServletRequestWrapper繼承了ServletRequestWrapper并且實現了HttpServletRequest接口。
ServletRequestWrapper已經幫我們實現了大部分的方法。
我們只需要將關心的幾個方法覆寫即可,如:getInputStream和getReader等。
當用戶嘗試調用getReader或getInputStream時,我們將之替換為自己的流,并且額外提供一個getContent()方法,將提前從StringBuilder或byte[]中讀取到的body內容進行提取。
編寫完自定義wrapper以后,我們就可以將其放入我們上文定義好的filter中,并將原request進行包裝替換,進而將用戶的request都變成我們的requestWrapper。
六、優化提取邏輯上文的方法相當于是將包含body的inputStream提前進行一次讀取,將其存儲在中間byte[]或StringBuilder當中,當用戶在調用getInputStream時,將byte[]或StringBuilder轉成inputStream返給用戶。如果用戶根本不關心本次http請求的body,即用戶根本沒有使用此次請求的body,那我們將其提前讀取出來相當于做了一次無用功(浪費了寶貴的CPU時間和內存資源)。如何保證只有在用戶使用時才讀取inputStream,并且當用戶或后續邏輯多次獲取body時都只讀一次是我們優化的目標。
答案還是繼續從源碼中尋找。既然我們的數據在inputStream中,那我們可以跟進源碼,看看inputStream是如何被讀取到的。在Servlet規范中,inputStream被封裝成了ServletInputStream,而ServletInputStream又提供了一個readLine方法。仔細觀察可以發現,他們都是調用了inputStream中的read方法,如下圖:
既然read方法是統一入口,是否只需要自定義實現一個ServletInputStream并覆寫其中的read()方法就能修改所有讀取方式了呢?答案是肯定的。只要在用戶調用read方法時,悄悄復制一份我們關心的內容,就能保證只有在用戶使用body時才讀取inputStream。
下一個問題就是如何保證在用戶多次調用read時只讀取一次inputStream。這里需要借助一個AtomicBoolean標志:當已經進行了一次完整讀取后,將其置為true;否則為false。最終效果如下:
七、舉一反三這里我們使用Servlet規范中的filter和wrapper機制來獲取進入我們容器(Tomcat)中所有Http請求的body和header。這個能力在實際生產中還能進一步拓展,如:傳輸某些敏感數據時,在Client端進行加密,然后在Server端統一解密,并格式化Client端上送的數據格式等。
讀完本文,大家應該能夠在不影響原代碼的前提下,通過簡單代碼獲取進入容器的所有Http請求的body和header。不過對于特殊技術棧,還需要進行適配。如果項目中使用了Jersey且使用application/x-www-form-urlencoded形式傳遞參數等信息,而服務端沒有使用@FormParam注解來獲取參數,那么獲取body以后用戶將無法獲取參數。但至少我們已經驗證了這條路是可行的,所以已經成功了一半。希望這份技術分享能夠在工作中幫到大家。
開源地址:https://github.com/uavorg/uav...
作者:李崇
來源:宜信技術學院
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76144.html
摘要:前言在從零開始實現一個簡易的框架七實現中實現了框架的的功能,不過最后指出代碼的邏輯不是很好,在這一章節就將這一部分代碼進行優化。 前言 在從零開始實現一個簡易的Java MVC框架(七)--實現MVC中實現了doodle框架的MVC的功能,不過最后指出代碼的邏輯不是很好,在這一章節就將這一部分代碼進行優化。 優化的目標是1.去除DispatcherServlet請求分發器中的http邏...
摘要:實現的四大模塊上文簡述了源碼的大體框架結構,接下來我們來實現一個的框架,筆者認為理解和實現一個框架需要實現四個大模塊,分別是封裝創建類構造函數構造對象中間件機制和剝洋蔥模型的實現錯誤捕獲和錯誤處理下面我們就逐一分析和實現。 什么是koa框架? ? ? ? ?koa是一個基于node實現的一個新的web框架,它是由express框架的原班人馬打造的。它的特點是優雅、簡潔、表達力強、自由度...
摘要:實現的四大模塊上文簡述了源碼的大體框架結構,接下來我們來實現一個的框架,筆者認為理解和實現一個框架需要實現四個大模塊,分別是封裝創建類構造函數構造對象中間件機制和剝洋蔥模型的實現錯誤捕獲和錯誤處理下面我們就逐一分析和實現。 什么是koa框架? ? ? ? ?koa是一個基于node實現的一個新的web框架,它是由express框架的原班人馬打造的。它的特點是優雅、簡潔、表達力強、自由度...
摘要:捕捉錯誤確保捕獲運行路由處理程序和中間件時發生的所有錯誤非常重要。對和的調用表明當前處理程序已完成并處于什么狀態,將跳過鏈中的所有剩余處理程序,除了那些設置為處理上述錯誤的處理程序。 錯誤處理 錯誤處理是指Express如何捕獲和處理同步和異步發生的錯誤,Express附帶一個默認的錯誤處理程序,因此你無需編寫自己的錯誤處理程序即可開始使用。 捕捉錯誤 確保Express捕獲運行路由處...
閱讀 617·2023-04-25 18:37
閱讀 2779·2021-10-12 10:12
閱讀 8312·2021-09-22 15:07
閱讀 563·2019-08-30 15:55
閱讀 3173·2019-08-30 15:44
閱讀 2194·2019-08-30 15:44
閱讀 1624·2019-08-30 13:03
閱讀 1560·2019-08-30 12:55