1. 概述
大多數 Web 應用程序都有執行諸如請求日誌記錄、驗證或身份驗證等操作的需求。更重要的是,這些 任務通常在 HTTP 端點的一組中共享。
好消息是,Spring Web 框架提供了一個 過濾機制,正是為了解決這個問題。
在本教程中,我們將學習如何為一組 URL 指定是否包含或排除 過濾器風格的任務的執行。
2. 過濾特定 URL
假設我們的 Web 應用程序需要記錄有關其請求的信息,例如它們的路徑和內容類型。一種方法是創建日誌過濾功能。
2.1. 日誌過濾器
首先,讓我們在 <em >LogFilter</em> 類中創建我們的日誌過濾器,該類繼承了 <em >OncePerRequestFilter</em> 類,並實現了 <em >doFilterInternal</em> 方法:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String path = request.getRequestURI();
String contentType = request.getContentType();
logger.info("Request URL path : {}, Request content type: {}", path, contentType);
filterChain.doFilter(request, response);
}2.1. 規則過濾器
假設我們需要確保日誌任務僅在特定 URL 模式下執行,即 /health</em/> 和 /faq/*.</em/>。為此,我們將使用 FilterRegistrationBean</em/> 註冊我們的日誌過濾器,使其僅匹配所需 URL 模式。
@Bean
public FilterRegistrationBean<LogFilter> logFilter() {
FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LogFilter());
registrationBean.addUrlPatterns("/health","/faq/*");
return registrationBean;
}2.2 排除過濾器
如果我們要排除URL在執行日誌任務時被執行,可以通過以下兩種方式輕鬆實現:
- 對於新的URL,請確保它不匹配過濾器使用的URL模式。
- 對於之前已啓用日誌記錄的舊URL,可以修改URL模式以排除該URL。
3. 篩選所有可能的 URL
我們輕鬆地滿足了在 LogFilter 中包含 URL 的先前用例,並且只需極小的努力。然而,如果 Filter 使用通配符 (*) 來匹配所有可能的 URL 模式,情況就會變得 複雜。
在這種情況下,我們需要自己編寫包含和排除邏輯。
3.1. 自定義過濾器
客户端可以通過請求頭向服務器發送有用的信息。 假設我們的 Web 應用程序目前僅在美國運行,這意味着我們不想處理來自其他國家/地區的請求。
假設我們的 Web 應用程序通過 X-Country-Code 請求頭指示區域。 因此,每個請求都包含此信息,這為使用過濾器提供了明確的理由。
讓我們實現一個 過濾器,該過濾器檢查請求頭,並拒絕不符合我們條件的請求:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String countryCode = request.getHeader("X-Country-Code");
if (!"US".equals(countryCode)) {
response.sendError(HttpStatus.BAD_REQUEST.value(), "Invalid Locale");
return;
}
filterChain.doFilter(request, response);
}3.2. 過濾器註冊
首先,我們使用星號 (*)通配符來註冊我們的過濾器,以匹配所有可能的 URL 模式:
@Bean
public FilterRegistrationBean<HeaderValidatorFilter> headerValidatorFilter() {
FilterRegistrationBean<HeaderValidatorFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new HeaderValidatorFilter());
registrationBean.addUrlPatterns("*");
return registrationBean;
}
稍後,我們可以排除不必要的 URL 模式,從而避免對驗證區域請求頭信息的任務造成不必要的負擔。
4. URL 排除
在本節中,我們將學習如何為我們的客户 過濾器 排除 URL。
4.1. 樸素策略
假設我們仍然有一個位於 /health 的 Web 路由,用於對應用程序進行乒乓健康檢查。
此前,所有請求都會觸發我們的過濾器。正如我們可以猜測的,這在健康檢查方面會帶來額外的開銷。
因此,為了簡化 /health 請求,我們將它們排除在主過濾器的作用範圍內:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String path = request.getRequestURI();
if ("/health".equals(path)) {
filterChain.doFilter(request, response);
return;
}
String countryCode = request.getHeader("X-Country-Code");
// ... same as before
}需要注意的是,在 doFilter 方法中添加這種自定義邏輯會使 /health 端點和我們的過濾器之間產生耦合。因此,這並不是最佳實踐,因為如果我們在不修改 doFilter 方法內部的代碼的情況下更改健康檢查端點,我們可能會破壞過濾邏輯。
4.2. 使用 shouldNotFilter 方法
採用前述方法,URL 排除與任務執行邏輯之間存在緊耦合,導致過濾器中可能意外引入錯誤,即使只是為了修改其中一個部分。
相反,我們可以通過重寫 <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.html#shouldNotFilter-javax.servlet.http.HttpServletRequest-">shouldNotFilter` 方法來隔離這兩種邏輯:
@Override
protected boolean shouldNotFilter(HttpServletRequest request)
throws ServletException {
String path = request.getRequestURI();
return "/health".equals(path);
}因此,doInternalFilter() 方法遵循 單一職責原則:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String countryCode = request.getHeader("X-Country-Code");
// ... same as before
}5. 結論
在本教程中,我們探討了如何在 Spring Boot Web 應用程序的 Servlet 過濾器中排除 URL 模式(模式)的方法,用於兩個用例:日誌記錄和請求頭驗證。
此外,我們瞭解到,對於使用通配符匹配所有可能 URL 模式的過濾器,排除特定的 URL 集合可能會變得複雜。