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------------------------------------------