0

使用 Scala 和 Play 2.5.10,我实现了以下可重用的组合操作,目的是通过更改响应标头来禁用浏览器中的缓存:

import play.api.http.HeaderNames
import play.api.mvc._

import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global

case class NoCache[A](action: Action[A]) extends Action[A] with HeaderNames {
  def apply(request: Request[A]): Future[Result] = {
    action(request).andThen {
      case Success(result) => result.withHeaders(
        (CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
        (PRAGMA -> "no-cache"),
        (EXPIRES -> "0")
      )
      case Failure(result) => result
    }
  }

  lazy val parser = action.parser
}

然后我在我的控制器操作实现中重用它,如下所示:

def link = NoCache {
  deadbolt.SubjectPresent()() { implicit request =>
    Future {
      Ok(views.html.account.link(userService, auth))
    }
  }
}

我在实现中设置断点NoCache并正确执行,但是,使用Web Developer Firefox 插件来监控网络流量我看到响应标头不包含“无缓存”修改......我做错了什么?

4

1 回答 1

1

你的代码有问题

问题出在andThen. andThen丢弃返回值。因此,转换result后的新标头将被丢弃。

删除andThen并使其成为map.

andThen用于在调用它的未来完成之后运行副作用计算。

的计算andThen返回一个与原始 future 结果相同的 Future 并丢弃内部计算的返回类型andThen

这是andThen来自标准库的实现。

 def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = {
    val p = Promise[T]()
    onComplete {
      case r => try pf.applyOrElse[Try[T], Any](r, Predef.conforms[Try[T]]) finally p complete r
    }
    p.future
  }

更正您的代码

case class NoCache[A](action: Action[A]) extends Action[A] with HeaderNames {
  def apply(request: Request[A]): Future[Result] = {
    action(request).map { result =>
      result.withHeaders(
        (CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
        (PRAGMA -> "no-cache"),
        (EXPIRES -> "0")
      )
    }
  }

  lazy val parser = action.parser
}

其他方法可以做到这一点

您可以使用播放过滤器Filter来更改标题,也可以用于进行一些预处理和后处理工作。

您可以通过检查请求的 uri 来仅定位特定路由。

import akka.stream.Materializer
import com.google.inject.{Inject, Singleton}
import play.api.http.DefaultHttpFilters
import play.api.mvc.{Filter, RequestHeader, Result}
import play.mvc.Http.HeaderNames

import scala.concurrent.Future

@Singleton
class Filters @Inject() (fooFilter: FooFilter) extends DefaultHttpFilters(fooFilter) {}

@Singleton
class FooFilter @Inject() (implicit override val mat: Materializer) extends Filter {
  override def apply(f: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] = {
    f(rh).map { result =>
      if (rh.uri.startsWith("/foo"))
      result.withHeaders(HeaderNames.CACHE_CONTROL -> "no-cache")
      else result
    }
  }
}

在上面的示例中,/foo将设置路由 cache_control,而对于其他路由,将传播相同的标头。

请注意在播放应用程序的根文件夹中创建过滤器,如果没有将过滤器添加到 application.conf

永远不要在过滤器内部运行繁重的计算,使过滤器尽可能轻。

于 2016-12-26T11:25:23.873 回答