§内置 HTTP 过滤器
Play 提供了几个标准过滤器,可以修改应用程序的 HTTP 行为。您也可以使用 Java 或 Scala 编写自己的过滤器。
§默认过滤器
Play 现在附带一组默认启用的过滤器,通过配置定义。如果属性 play.http.filters
为空,则默认值为 play.api.http.EnabledFilters
,它会加载在 play.filters.enabled
配置属性中通过完全限定类名定义的过滤器。
在 Play 本身中,play.filters.enabled
是一个空列表。但是,过滤器库会作为名为 PlayFilters
的 AutoPlugin 自动加载到 sbt 中,并将以下值追加到 play.filters.enabled
属性
play.filters.csrf.CSRFFilter
play.filters.headers.SecurityHeadersFilter
play.filters.hosts.AllowedHostsFilter
这意味着在新的项目中,CSRF 保护 (ScalaCsrf / JavaCsrf)、SecurityHeaders 和 AllowedHostsFilter 都已自动定义。
要追加到默认列表,请使用 +=
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 标头。
如果您使用的是FakeRequest
或Helpers.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.NoHttpFiltersComponents
和 play.filters.components.NoHttpFiltersComponents
都有 httpFilters
方法,该方法返回一个空的过滤器列表。
下一步:配置 gzip 编码
发现此文档中的错误?此页面的源代码可以在 此处 找到。阅读完 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?请访问 我们的社区论坛 与社区展开对话。