前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PlayScala 2.5.x - Filter开发指南

PlayScala 2.5.x - Filter开发指南

作者头像
joymufeng
发布2018-05-17 15:42:19
1K0
发布2018-05-17 15:42:19
举报

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:

代码语言:javascript
复制
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类:

代码语言:javascript
复制
class Filters @Inject() (
  log: LoggingFilter
) extends HttpFilters {
  val filters = Seq(log)
}

启动应用,在控制台查看日志输出。

4. 深入剖析

示例代码请参考这里,示例中定义了三个Filter,分别是Filter1, Filter2和Filter3,每个Filter在接收到请求和响应的时候会打印信息到控制台,在Filter链中的定义顺序如下:

代码语言:javascript
复制
Seq(filter1, filter2, filter3)

针对一次请求控制台输出如下:

代码语言:javascript
复制
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中被调用的,代码如下:

代码语言:javascript
复制
  /**
   * Apply filters to the given action.
   */
  protected def filterAction(next: EssentialAction): EssentialAction = {
    filters.foldRight(next)(_ apply _)
  }

filters.foldRight的调用过程如下:

代码语言:javascript
复制
     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. 参考

Play Framework - Filters

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Filter简介
  • 2. Filters vs Action Composition vs Request Handler
    • 1)  router调用次序不同
      • 2)  关注点不同
      • 3. 使用Filter
      • 4. 深入剖析
      • 5. 灵活使用Filter
      • 6. 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档