文件

§內建 HTTP 篩選器

Play 提供多個標準篩選器,可以修改應用程式的 HTTP 行為。您也可以使用 JavaScala 編寫自己的篩選器。

§預設篩選器

Play 現在附帶一組預設啟用的篩選器,透過設定定義。如果屬性 play.http.filters 為 null,則預設值現在為 play.api.http.EnabledFilters,它會載入 play.filters.enabled 設定屬性中以完全限定類別名稱定義的篩選器。

在 Play 本身中,play.filters.enabled 是個空清單。不過,這個 filter 函式庫會在 sbt 中自動載入為稱為 PlayFilters 的 AutoPlugin,並會將下列值附加到 play.filters.enabled 屬性

這表示在新的專案中,CSRF 保護 (ScalaCsrf / JavaCsrf)、SecurityHeadersAllowedHostsFilter 都會自動定義。

若要附加到預設清單,請使用 +=

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 標頭。

如果您使用 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")
      }
    }
  }
}

§編譯時間預設篩選器

如果您使用編譯時間依賴性注入,則預設篩選器會在編譯時間解析,而不是在執行時間解析。

這表示 (針對 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.NoHttpFiltersComponentsplay.filters.components.NoHttpFiltersComponents 都有 httpFilters 方法,可傳回一個空的篩選器清單。

下一步:設定 gzip 編碼


在此文件中發現錯誤?此頁面的原始碼可在此處找到 here。閱讀 文件指南 後,歡迎您貢獻一個 pull request。有問題或建議要分享嗎?請前往 我們的社群論壇 與社群展開對話。