§內建 HTTP 篩選器
Play 提供多個標準篩選器,可以修改應用程式的 HTTP 行為。您也可以使用 Java 或 Scala 編寫自己的篩選器。
§預設篩選器
Play 現在附帶一組預設啟用的篩選器,透過設定定義。如果屬性 play.http.filters
為 null,則預設值現在為 play.api.http.EnabledFilters
,它會載入 play.filters.enabled
設定屬性中以完全限定類別名稱定義的篩選器。
在 Play 本身中,play.filters.enabled
是個空清單。不過,這個 filter 函式庫會在 sbt 中自動載入為稱為 PlayFilters
的 AutoPlugin,並會將下列值附加到 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
來定義自己的 filter,您也可以在程式碼中將 EnabledFilters
與您自己的 filter 結合
- 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
功能。
§測試預設 Filter
由於啟用了多個 filter,功能測試可能需要稍作變更,以確保所有測試都能通過且要求有效。例如,要求未將 Host
HTTP 標頭設定為 localhost
的要求將無法通過 AllowedHostsFilter,並會傳回 400 禁止回應。
§使用 AllowedHostsFilter 進行測試
由於 AllowedHostsFilter filter 會自動加入,功能測試需要加入 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")
}
}
}
}
§編譯時間預設篩選器
如果您使用編譯時間依賴性注入,則預設篩選器會在編譯時間解析,而不是在執行時間解析。
這表示 (針對 Scala) play.api.BuiltInComponents
特質和 (針對 Java) play.BuiltInComponents
介面現在包含一個 httpFilters
方法,該方法會保持抽象。預設篩選器清單定義在 (針對 Scala) play.filters.HttpFiltersComponents
和 (針對 Java) play.filters.components.HttpFiltersComponents
中。因此,在大部分情況下,您會想要混合 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 編碼
在此文件中發現錯誤?此頁面的原始碼可在此處找到 here。閱讀 文件指南 後,歡迎您貢獻一個 pull request。有問題或建議要分享嗎?請前往 我們的社群論壇 與社群展開對話。