文档

§内置 HTTP 过滤器

Play 提供了几个标准过滤器,可以修改应用程序的 HTTP 行为。您也可以使用 JavaScala 编写自己的过滤器。

§默认过滤器

Play 现在附带一组默认启用的过滤器,通过配置定义。如果属性 play.http.filters 为空,则默认值为 play.api.http.EnabledFilters,它会加载在 play.filters.enabled 配置属性中通过完全限定类名定义的过滤器。

在 Play 本身中,play.filters.enabled 是一个空列表。但是,过滤器库会作为名为 PlayFilters 的 AutoPlugin 自动加载到 sbt 中,并将以下值追加到 play.filters.enabled 属性

这意味着在新的项目中,CSRF 保护 (ScalaCsrf / JavaCsrf)、SecurityHeadersAllowedHostsFilter 都已自动定义。

要追加到默认列表,请使用 +=

play.filters.enabled+=MyFilter

如果您之前通过扩展 play.api.http.DefaultHttpFilters 定义了自己的过滤器,那么您也可以在代码中将 EnabledFilters 与您自己的过滤器组合起来

Java
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import play.api.http.EnabledFilters;
import play.filters.cors.CORSFilter;
import play.http.DefaultHttpFilters;
import play.mvc.EssentialFilter;

public class Filters extends DefaultHttpFilters {

  @Inject
  public Filters(EnabledFilters enabledFilters, CORSFilter corsFilter) {
    super(combine(enabledFilters.asJava().getFilters(), corsFilter.asJava()));
  }

  private static List<EssentialFilter> combine(
      List<EssentialFilter> filters, EssentialFilter toAppend) {
    List<EssentialFilter> combinedFilters = new ArrayList<>(filters);
    combinedFilters.add(toAppend);
    return combinedFilters;
  }
}
Scala
import javax.inject.Inject

import play.api.http.DefaultHttpFilters
import play.api.http.EnabledFilters
import play.filters.cors.CORSFilter

class Filters @Inject() (enabledFilters: EnabledFilters, corsFilter: CORSFilter)
    extends DefaultHttpFilters(enabledFilters.filters :+ corsFilter: _*)

否则,如果您在根目录中有一个Filters类或显式定义了play.http.filters,它将优先于下面描述的EnabledFilters功能。

§测试默认过滤器

由于启用了多个过滤器,功能测试可能需要稍作更改以确保所有测试都通过且请求有效。例如,没有将Host HTTP 标头设置为localhost的请求将无法通过AllowedHostsFilter,并将返回 400 Forbidden 响应。

§使用 AllowedHostsFilter 进行测试

由于 AllowedHostsFilter 过滤器是自动添加的,因此功能测试需要添加 Host HTTP 标头。

如果您使用的是FakeRequestHelpers.fakeRequest,则会自动为您添加Host HTTP 标头。如果您使用的是play.mvc.Http.RequestBuilder,则可能需要添加自己的行以手动添加标头。

Http.RequestBuilder request =
    new Http.RequestBuilder()
        .method(GET)
        .header(Http.HeaderNames.HOST, "localhost")
        .uri("/xx/Kiwi");

§使用 CSRFFilter 进行测试

由于 CSRFFilter 过滤器是自动添加的,因此渲染包含CSRF.formField的 Twirl 模板的测试,即

@(userForm: Form[UserData])(implicit request: RequestHeader, m: Messages)

<h1>user form</h1>

@request.flash.get("success").getOrElse("")

@helper.form(action = routes.UserController.userPost()) {
  @helper.CSRF.formField
  @helper.inputText(userForm("name"))
  @helper.inputText(userForm("age"))
  <input type="submit" value="submit"/>
}

必须在请求中包含 CSRF 令牌。在 Scala API 中,这是通过导入play.api.test.CSRFTokenHelper._ 来完成的,它使用withCSRFToken方法丰富了play.api.test.FakeRequest

import org.specs2.mutable.Specification
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.test.CSRFTokenHelper._
import play.api.test.FakeRequest
import play.api.test.Helpers._
import play.api.test.WithApplication

class UserControllerSpec extends Specification {
  "UserController GET" should {
    "render the index page from the application" in new WithApplication() {
      override def running() = {
        val controller = app.injector.instanceOf[UserController]
        val request    = FakeRequest().withCSRFToken
        val result     = controller.userGet().apply(request)

        status(result) must beEqualTo(OK)
        contentType(result) must beSome("text/html")
      }
    }
  }
}

在 Java API 中,这是通过在play.mvc.Http.RequestBuilder实例上调用CSRFTokenHelper.addCSRFToken来完成的。

Http.RequestBuilder request = new Http.RequestBuilder().method(POST).uri("/xx/Kiwi");

request = CSRFTokenHelper.addCSRFToken(request);

§禁用默认过滤器

禁用过滤器的最简单方法是将其添加到application.conf中的play.filters.disabled列表中。

play.filters.disabled+=play.filters.hosts.AllowedHostsFilter

如果您有不想经过默认过滤器的功能测试,这可能很有用。

要删除默认过滤器,您可以手动设置整个列表。

play.filters.enabled=[]

如果您想删除所有过滤器类,可以通过disablePlugins机制禁用它。

lazy val root = project.in(file(".")).enablePlugins(PlayScala).disablePlugins(PlayFilters)

如果您正在编写涉及GuiceApplicationBuilder的功能测试,则可以通过调用configure在测试中禁用所有过滤器。

class UserControllerWithoutFiltersSpec extends Specification {
  "UserControllerWithoutFiltersSpec GET" should {
    "render the index page from the application" in new WithApplication(
      GuiceApplicationBuilder().configure("play.http.filters" -> "play.api.http.NoHttpFilters").build()
    ) {
      override def running() = {
        val controller = app.injector.instanceOf[UserController]
        val request    = FakeRequest().withCSRFToken
        val result     = controller.userGet().apply(request)

        status(result) must beEqualTo(OK)
        contentType(result) must beSome("text/html")
      }
    }
  }
}

§编译时默认过滤器

如果您使用的是编译时依赖注入,则默认过滤器是在编译时解析的,而不是通过运行时解析的。

这意味着play.api.BuiltInComponents特征(对于 Scala)和play.BuiltInComponents接口(对于 Java)现在包含一个httpFilters方法,该方法是抽象的。默认过滤器列表在play.filters.HttpFiltersComponents(对于 Scala)和play.filters.components.HttpFiltersComponents(对于 Java)中定义。因此,在大多数情况下,您需要混合HttpFiltersComponents并追加您自己的过滤器。

Java
import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.filters.components.HttpFiltersComponents;
import play.mvc.EssentialFilter;
import play.routing.Router;

import java.util.ArrayList;
import java.util.List;

public class MyAppComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents {

  public MyAppComponents(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public List<EssentialFilter> httpFilters() {
    List<EssentialFilter> combinedFilters =
        new ArrayList<>(HttpFiltersComponents.super.httpFilters());
    combinedFilters.add(new LoggingFilter(materializer()));
    return combinedFilters;
  }

  @Override
  public Router router() {
    return Router.empty(); // implement the router as needed
  }
}
Scala
import org.apache.pekko.util.ByteString
import play.api.libs.streams.Accumulator
import play.api.mvc.EssentialAction
import play.api.mvc.EssentialFilter
import play.api.mvc.RequestHeader
import play.api.mvc.Result
import play.api.routing.Router
import play.api.ApplicationLoader
import play.api.BuiltInComponentsFromContext
import play.api.NoHttpFiltersComponents
import play.filters.csrf.CSRFFilter

class MyAppComponents(context: ApplicationLoader.Context)
    extends BuiltInComponentsFromContext(context)
    with play.filters.HttpFiltersComponents {
  lazy val loggingFilter = new LoggingFilter()

  override def httpFilters: Seq[EssentialFilter] = {
    super.httpFilters :+ loggingFilter
  }

  override def router: Router = Router.empty // implement the router as needed
}

如果您想从列表中过滤元素,您可以执行以下操作。

Java
public class MyAppComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents {

  public MyAppComponents(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public List<EssentialFilter> httpFilters() {
    return HttpFiltersComponents.super.httpFilters().stream()
        .filter(filter -> !filter.getClass().equals(CSRFFilter.class))
        .collect(Collectors.toList());
  }

  @Override
  public Router router() {
    return Router.empty(); // implement the router as needed
  }
}
Scala
class MyAppComponents(context: ApplicationLoader.Context)
    extends BuiltInComponentsFromContext(context)
    with play.filters.HttpFiltersComponents {
  override def httpFilters: Seq[EssentialFilter] = {
    super.httpFilters.filterNot(_.getClass == classOf[CSRFFilter])
  }

  override def router: Router = Router.empty // implement the router as needed
}

§禁用编译时默认过滤器

要禁用默认过滤器,请在 Scala 中混合使用 play.api.NoHttpFiltersComponents,在 Java 中混合使用 play.filters.components.NoHttpFiltersComponents

Java
public class MyAppComponents extends BuiltInComponentsFromContext
    implements NoHttpFiltersComponents {

  public MyAppComponents(ApplicationLoader.Context context) {
    super(context);
  }

  // no need to override httpFilters method

  @Override
  public Router router() {
    return Router.empty(); // implement the router as needed
  }
}
Scala
class MyAppComponents(context: ApplicationLoader.Context)
    extends BuiltInComponentsFromContext(context)
    with NoHttpFiltersComponents {
  override def router: Router = Router.empty // implement the router as needed
}

Scala play.api.NoHttpFiltersComponentsplay.filters.components.NoHttpFiltersComponents 都有 httpFilters 方法,该方法返回一个空的过滤器列表。

下一步:配置 gzip 编码


发现此文档中的错误?此页面的源代码可以在 此处 找到。阅读完 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?请访问 我们的社区论坛 与社区展开对话。