Filter
Filter,顧名思義,過濾器,是由servlet容器為每個傳入的http請求和每個http響應執行的Java類。 這樣,就可以在HTTP傳入請求到達資源之前對其進行管理,例如JSP頁面,servlet或簡單的靜態頁面; 以相同的方式可以在資源執行後管理HTTP出站響應。
此行為允許實現在許多不同上下文中重用的常用功能。
Filter在只在 Servlet 前後起作用。Filters 通常將請求和響應(request/response) 當做黑盒子,Filter 通常不考慮servlet的實現。
如上圖所示,過濾器在Web容器中執行,因此其定義也將包含在web.xml檔案中。
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>com.listfeeds.components.CORSFilter</filter-class>
<init-param>
<param-name>fake-param</param-name>
<param-value>fake-param-value</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
複製程式碼
在過濾器定義中,由com.listfeeds.filters.CORSFilter類實現的過濾器具有滿足表示式的所有端點:/ *(在本例中為all)
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
複製程式碼
Filter包括三個主要方法:
- init:執行以使用過濾器定義中的init-param元素初始化過濾器
- doFilter:為滿足“url-pattern”的所有HTTP傳入請求執行
- destroy:釋放過濾器使用的資源
Filter使用場景:
- 身份驗證:根據使用者身份阻止請求。
- 記錄日誌和稽核:跟蹤Web應用程式的使用者。
- 影像轉換:縮放地圖等。
- 資料壓縮:使下載量更小。
- 本地化:將請求和響應定位到特定區域設定。
Request Filters 可以:
- 執行安全檢查
- 重新格式化請求標頭或正文
- 請求記錄與審計
Response Filters 可以:
- 壓縮響應流
- 追加或改變響應流
- 完全創造一個不同的回應
Filter的輪子有:
- 認證 Filters
- 日誌和請求審計 Filters
- 影像轉換 Filters
- 資料壓縮 Filters
- 加密 FIlter
- ...
Interceptor
Spring攔截器類似於Servlet過濾器,但它們在Spring Context中起作用,因此管理HTTP請求和響應的功能非常強大,但它們可以實現更加軟化的行為,因為它可以訪問所有Spring上下文。
Interceptor能夠深入到方法前後、異常丟擲前後等,因此攔截器的使用具有更大的彈性。允許使用者介入(hook into)請求的生命週期,在請求過程中獲取資訊,Interceptor 通常和請求更加耦合。
Spring攔截器在SpringMVC上下文中執行,因此它們已在spring-servlet.xml檔案中定義:
<mvc:interceptors>
<bean class="com.listfeeds.interceptors.LogContextInterceptor" />
<bean class="com.listfeeds.interceptors.TimedInterceptor" />
</mvc:interceptors>
複製程式碼
com.listfeeds.interceptors.LogContextInterceptor攔截器類,用於向Log4j Thread上下文新增引數。
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LogContextInterceptor extends HandlerInterceptorAdapter {
private static final Logger log = LoggerFactory.getLogger(LogContextInterceptor.class);
public static final String LOG_IDENTIFYING_TOKEN = "logIdentifyingToken";
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
HandlerMethod methodHandler = (HandlerMethod) handler;
log.debug("END EXECUTION method {} request: {}", methodHandler.getMethod().getName(), request.getRequestURI());
Boolean settato = (Boolean) request.getAttribute(LOG_IDENTIFYING_TOKEN);
if(settato != null && settato) {
MDC.remove(LOG_IDENTIFYING_TOKEN);
}
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
try {
if( MDC.get(LOG_IDENTIFYING_TOKEN) == null ) {
/* Retrieve parameters useful for logging */
@SuppressWarnings("unchecked")
Map<String,String> pathVariables =
(Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String applicationId = null;
if ( pathVariables != null )
applicationId = pathVariables.get("applicationId");
if ( StringUtils.isEmpty(applicationId) )
applicationId = request.getParameter("applicationId");
String loggingToken =
String.format("ApplicationId: %s", applicationId);
MDC.put(LOG_IDENTIFYING_TOKEN, loggingToken);
request.setAttribute(LOG_IDENTIFYING_TOKEN, Boolean.TRUE);
}
}
catch ( IllegalArgumentException e)
{
log.warn("Prehandle",e);
return true;
}
finally {
HandlerMethod methodHandler = (HandlerMethod) handler;
//logger.debug("START EXECUTION " + methodHandler.getMethod().getName());
log.debug("START EXECUTION method {} request: {}", methodHandler.getMethod().getName(), request.getRequestURI());
}
return true;
}
}
複製程式碼
interceptor 包含如下幾個主要方法:
- preHandle:在執行目標資源之前執行
- afterCompletion:執行目標資源後執行(渲染檢視後)
- posttHandle:攔截處理程式的執行