1. Filter简介
Filter是Play基于责任链模式(Chain of Responsibility)实现的过滤器,利用Filter可以过滤所有的请求和响应。Play的Filter实现非常灵活,你可以在Filter中修改请求和响应,或终止Filter链的传递,直接返回响应。Filter常用于以下几种场景:
- 打印请求日志
- 统计请求信息
- 启用Gzip压缩
- 添加安全响应头
- 实现全局缓存
Play中实现的Filter API有两个,分别是EssentialFilter和Filter。其中EssentialFilter是底层API,功能更加强大,而Filter是基于EssentialFilter实现的用于简化开发的类,主要目的简化接口实现,隐藏Request Body的处理。本文的示例均使用底层的EssentialFilter实现。
2. Filters vs Action Composition vs Request Handler
1) router调用次序不同
Filters和Action Composition发生在router调用之后,二者无法改变Request Path,但是仍然可以修改Request Headers和Body;Request Handler发生在router调用之前,可以通过修改Request Path将请求重定向到特定的Action。
2) 关注点不同
Request Handler关注点在于过滤非法请求或重定向路由;Filter的关注点在于请求和响应的过滤处理;Action Composition主要关注点在于权限验证和授权。
3. 使用Filter
首先基于EssentialFilter实现一个LoggingFilter:
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类:
class Filters @Inject() (
log: LoggingFilter
) extends HttpFilters {
val filters = Seq(log)
}
启动应用,在控制台查看日志输出。
4. 深入剖析
示例代码请参考这里,示例中定义了三个Filter,分别是Filter1, Filter2和Filter3,每个Filter在接收到请求和响应的时候会打印信息到控制台,在Filter链中的定义顺序如下:
Seq(filter1, filter2, filter3)
针对一次请求控制台输出如下:
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。
我们从源码来寻找一些蛛丝马迹,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的执行过程看起来就像是一个递归调用,这也就解释了上面的控制台输出结果。
5. 灵活使用Filter
从上面的分析结果可以看出,Filter在Filter Chain中的顺序是很重要的,放错位置就会得到意想不到的结果。需要修改所有响应的Filter应该放在最前面,例如Gzip Filter,Security Headers Filter。因为其它的Filter可能会终止Filter Chain的传递直接返回响应,如果将Gzip Filter放在其后面,将导致Gzip Filter没有机会修改响应结果,从而导致返回非压缩响应。
-------------------------------------------------转载请注明作者joymufeng------------------------------------------
6. 参考
来源:oschina
链接:https://my.oschina.net/u/659771/blog/685374