過濾器(Filter)是什麼?
過濾器是 J2EE 標準規範功能,可在分派器 Servlet(Dispatcher Servlet) 接收請求之前/之後,針對符合 URL 模式的所有請求處理額外作業。也就是說,它由 Tomcat 等 Web 容器而非 Spring 容器管理,因此在傳遞到分派器 Servlet 之前處理請求。
過濾器(Filter) 實作
若要新增過濾器,必須實作 javax.servlet 的 Filter 介面,此介面包含以下 3 個方法。
- init 方法
- 用於初始化過濾器物件並新增至服務。Web 容器會呼叫 init 方法一次來初始化過濾器物件,之後的請求會透過 doFilter() 方法處理。
- doFilter 方法
- 當符合 url-pattern 的所有 HTTP 請求在傳遞到分派器 Servlet 之前,會由 Web 容器執行,以及在分派器 Servlet 將 HTTP 回應傳遞給用戶端之前,由 Web 容器執行的方法。
doFilter() 的參數包含 FilterChain,透過 FilterChain 的 doFilter() 將請求傳遞到下一個目標。藉由在 chain.doFilter() 之前/之後加入我們需要的處理程序,即可執行所需的處理。
- 當符合 url-pattern 的所有 HTTP 請求在傳遞到分派器 Servlet 之前,會由 Web 容器執行,以及在分派器 Servlet 將 HTTP 回應傳遞給用戶端之前,由 Web 容器執行的方法。
- destory 方法
- 用於從服務移除過濾器物件並傳回所使用的資源。Web 容器會呼叫此方法一次,之後將不再透過 doFilter() 處理。
範例程式碼 - Servlet 規範
範例程式碼 - @Component
- @Component:可將 Filter 註冊為 Spring Bean。
- @Order:如果有多個過濾器,則可以使用此註解指定執行順序。
- 如果將符合 Servlet 規範的過濾器註冊為 Spring Bean,則可以使用其他符合 Spring 規範的 Bean。
範例程式碼 - @Configuration
如果要讓過濾器僅作用於特定 URI,則可以使用 FilterRegistrationBean 將過濾器註冊為 Spring Bean。
過濾器(Filter) 用途
主要負責驗證和處理請求參數本身。
- 與安全性相關的共用作業
- 由於過濾器在 Web 容器中執行,因此可以執行安全性檢查(例如 XSS、CSRF 防禦),並在請求不正確時進行阻擋。由於請求不會傳遞到 Spring 容器,因此可以進一步提高安全性。
- 所有請求的記錄
- 影像/資料壓縮和字串編碼
- 過濾器實作了影像或資料壓縮以及字串編碼等 Web 應用程式普遍使用的功能。
- 自訂 ServletRequest
- HttpServletRequest 只能讀取主體內容一次。因此,Filter 或 Interceptor 無法讀取主體。可以建立自訂 ServletRequest 來記錄主體。
攔截器(Interceptor)
攔截器(Interceptor)是什麼?
攔截器(Interceptor) 與 J2EE 標準規範的過濾器(Filter) 不同,它是 Spring 提供的技術,可在分派器 Servlet 呼叫控制器之前和之後參照或處理請求和回應。也就是說,攔截器在 Spring 內容中執行,而非在 Web 容器中執行。
分派器 Servlet 會透過處理器對應來尋找適當的控制器以處理請求,並傳回執行鏈(HandlerExecutionChain)。因此,如果此執行鏈註冊了 1 個或多個攔截器,則會依序執行攔截器,然後執行控制器;如果沒有攔截器,則會直接執行控制器。
攔截器(Interceptor) 實作
若要新增攔截器,必須實作 org.springframework.web.servlet.HandlerInterceptor 介面,此介面包含以下 3 個方法。
- preHandle 方法
- preHandle 方法會在呼叫控制器之前執行。因此,如果需要在控制器之前處理前置作業或處理和新增請求資訊,則可以使用此方法。
- preHandle 方法的第 3 個參數 handler 是 @RequestMapping 註解的方法資訊的抽象物件。
- preHandle 方法的傳回類型為布林值,如果傳回 true,則會繼續執行下一個步驟;如果傳回 false,則會停止作業,並停止執行後續作業(下一個攔截器或控制器)。
- postHandle 方法
- postHandle 方法會在呼叫控制器之後執行。因此,如果需要在控制器之後處理後置作業,則可以使用此方法。
- afterCompletion 方法
- afterCompletion 方法顧名思義,是在所有作業(包括所有檢視產生最終結果的作業)完成後執行。
範例程式碼
- 將建立的攔截器註冊為 Bean。
- 在 WebMvcConfigurer 介面的 addInterceptors() 方法中註冊建立的攔截器。
- 攔截器會按照在 InterceptorRegistry 中註冊的順序執行。
攔截器(Interceptor) 用途
主要負責使用服務邏輯處理請求資訊的一致性。
- 驗證/授權等共用作業
- 例如,可以在控制器接收到請求之前檢查驗證或授權,這些是與用戶端請求相關的典型作業。
- API 呼叫記錄
- 可以使用接收到的 HttpServletRequest 和 HttpServletResponse 物件來記錄用戶端資訊。
- 處理傳遞給控制器的資料
- 可以處理接收到的 HttpServletRequest 和 HttpServletResponse 物件,並將其傳遞給控制器。
- 模擬 AOP
- 可以使用 preHandle() 方法的第 3 個參數 HandlerMethod 來取得即將執行的函數的簽章等額外資訊,並據此判斷是否執行邏輯。
過濾器(Filter) 與攔截器(Interceptor) 的差異
- 過濾器在 Web 內容中執行,攔截器在 Spring 內容中執行,因此執行時機不同。
- 過濾器可以處理分派器 Servlet 的前後,攔截器可以處理控制器的前後。
- 過濾器與 Spring 無關,因此適合用於處理需要全域處理的作業,或驗證輸入的參數本身,或者在使用 ServletRequest 而不是 HttpServletRequest 的情況下使用。
- 攔截器與 Spring 息息相關,因此適合用於處理需要全域處理與用戶端請求相關的作業,或者需要混合使用服務邏輯時使用。
- 攔截器可以使用 @ControllerAdvice 和 @ExceptionHandler 來處理例外,但過濾器無法使用它們。
- 過濾器通常會將 doFilter() 方法用 try~catch 語法括起來,並在該時機點直接處理發生的例外。
參數解析器(Argument Resolver)
參數解析器(Argument Resolver) 是什麼?
參數解析器可以間接地協助我們在請求傳遞到控制器時,從請求中獲取的值建立所需的物件。
參數解析器(Argument Resolver) 用途
例如,假設請求中包含 JWT 權杖。我們需要驗證此權杖是否有效,然後提取權杖中儲存的 ID,並將其轉換為登入使用者物件。
如果沒有使用參數解析器,則需要在每個控制器中實作權杖驗證和將其轉換為登入使用者物件的過程。這樣會導致需要使用者驗證的控制器出現重複程式碼,並增加控制器的責任。參數解析器可以解決此問題。
參數解析器(Argument Resolver) 實作
可以透過實作 HandlerMethodArgumentResolver 來使用參數解析器。此介面要求實作以下兩個方法。
- supportsParameter 方法
- 在想要執行參數解析器的參數前建立並附加特定的註解。supportsParameter() 方法會檢查請求的方法的參數是否附帶所需的註解,如果包含所需的註解,則會傳回 true。
- resolveArgument 方法
- 如果 supportsParameter 傳回 true,也就是說,如果存在附帶特定註解的方法,則此方法會將 parameter 繫結到所需的格式,並傳回結果。
使用參數解析器時,控制器的實作如下所示。
參數解析器與攔截器(Interceptor) 的差異
- 參數解析器在攔截器之後執行,並且在請求傳遞到控制器時,從請求中獲取的值建立所需的物件。
- 另一方面,攔截器會在控制器實際執行之前攔截請求,並且無法傳回任何物件。它只能傳回布林值或 void 類型。
資料來源
- https://mangkyu.tistory.com/173
- https://junhyunny.github.io/spring-boot/filter-interceptor-and-aop/
- https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/
- https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/
- https://baeldung-cn.com/spring-mvc-handlerinterceptor-vs-filter
- https://www.baeldung.com/spring-boot-add-filter
預期面試問題與回答
過濾器是什麼?
過濾器是在分派器 Servlet 接收請求之前/之後,針對符合 URL 模式的所有請求處理額外作業的功能,並且在 Servlet 容器中執行。
過濾器何時使用?
過濾器可用於處理需要在 Spring 框架之外全域處理的作業。或者,在驗證請求參數時使用。
例如,處理與安全性相關的共用作業、所有請求的記錄、影像/資料壓縮和字串編碼、以及自訂 ServletRequest 作業。
攔截器是什麼?
攔截器是在分派器 Servlet 呼叫控制器之前和之後參照或處理請求和回應的功能,也就是說,它在 Spring 容器中執行。
攔截器何時使用?
攔截器可用於處理需要全域處理與用戶端請求相關的作業,以及在需要呼叫服務邏輯進行驗證時使用。
例如,處理驗證/授權等共用作業、API 呼叫記錄、以及處理傳遞給控制器的資料作業。
過濾器與攔截器的差異?
過濾器在 Servlet 容器中執行,攔截器在 Spring 容器中執行。
過濾器可以處理分派器 Servlet 的前後,攔截器可以處理控制器的前後。
因此,過濾器適合用於處理需要在 Spring 框架之外全域處理的作業,攔截器適合用於處理需要全域處理與用戶端請求相關的作業。
參數解析器是什麼?
參數解析器是在請求傳遞到控制器時,從請求中獲取的值建立所需的物件的功能。
參數解析器何時使用?
例如,在請求中包含 JWT 權杖時,可以驗證權杖是否有效,然後提取權杖中儲存的 ID,並將其轉換為 LoginMember 物件。
攔截器與參數解析器的差異?
參數解析器是在請求傳遞到控制器時,從請求中獲取的值建立所需的物件。另一方面,攔截器無法傳回物件,並且在攔截器執行後才會執行參數解析器。
评论0