Filter是Play基於責任鏈模式(Chain of Responsibility)實現的過濾器,利用Filter能夠過濾全部的請求和響應。Play的Filter實現很是靈活,你能夠在Filter中修改請求和響應,或終止Filter鏈的傳遞,直接返回響應。Filter經常使用於如下幾種場景:git
Play中實現的Filter API有兩個,分別是EssentialFilter和Filter。其中EssentialFilter是底層API,功能更增強大,而Filter是基於EssentialFilter實現的用於簡化開發的類,主要目的簡化接口實現,隱藏Request Body的處理。本文的示例均使用底層的EssentialFilter實現。github
Filters和Action Composition發生在router調用以後,兩者沒法改變Request Path,可是仍然能夠修改Request Headers和Body;Request Handler發生在router調用以前,能夠經過修改Request Path將請求重定向到特定的Action。緩存
Request Handler關注點在於過濾非法請求或重定向路由;Filter的關注點在於請求和響應的過濾處理;Action Composition主要關注點在於權限驗證和受權。安全
首先基於EssentialFilter實現一個LoggingFilter:app
class LoggingFilter @Inject() (implicit ec: ExecutionContext) extends EssentialFilter { def apply(nextFilter: EssentialAction) = new EssentialAction { def apply(requestHeader: RequestHeader) = { val startTime = System.currentTimeMillis nextFilter(requestHeader).map { result => val requestTime = System.currentTimeMillis - startTime Logger.info(s"${requestHeader.path} took ${requestTime}ms") result.withHeaders("Request-Time" -> requestTime.toString) } } } }
在root package先添加一個Filters類:spa
class Filters @Inject() ( log: LoggingFilter ) extends HttpFilters { val filters = Seq(log) }
啓動應用,在控制檯查看日誌輸出。scala
示例代碼請參考這裏,示例中定義了三個Filter,分別是Filter1, Filter2和Filter3,每一個Filter在接收到請求和響應的時候會打印信息到控制檯,在Filter鏈中的定義順序以下:日誌
Seq(filter1, filter2, filter3)
針對一次請求控制檯輸出以下:code
Filter1 receive request Filter2 receive request Filter3 receive request Filter3 receive response Filter2 receive response Filter1 receive response
上面的輸出頗有意思,看起來很像一次遞歸調用,Filter1遞進調用Filter2, Filter2遞進調用Filter3,Filter3取回結果迴歸到Filter2,Filter2取回結果迴歸到Filter1。router
咱們從源碼來尋找一些蛛絲馬跡,Filter Chain是在HttpRequestHandler中被調用的,代碼以下:
/** * Apply filters to the given action. */ protected def filterAction(next: EssentialAction): EssentialAction = { filters.foldRight(next)(_ apply _) }
filters.foldRight的調用過程以下:
apply / \ filter1 apply / \ filter2 apply / \ filter3 EssentialAction
上圖中apply節點的類型爲EssentialAction,每一個apply節點是其下EssentialAction的delegator對象。最上面的apply節點返回的EssentialAction即是Filter Chain的執行起點,因爲最終的Result是由右下方的EssentialAction執行生成的,因此整個Filter Chain的執行過程看起來就像是一個遞歸調用,這也就解釋了上面的控制檯輸出結果。
從上面的分析結果能夠看出,Filter在Filter Chain中的順序是很重要的,放錯位置就會獲得意想不到的結果。須要修改全部響應的Filter應該放在最前面,例如Gzip Filter,Security Headers Filter。由於其它的Filter可能會終止Filter Chain的傳遞直接返回響應,若是將Gzip Filter放在其後面,將致使Gzip Filter沒有機會修改響應結果,從而致使返回非壓縮響應。
-------------------------------------------------轉載請註明做者joymufeng------------------------------------------