api过滤器_了解播放过滤器API
api過濾器
隨著Play 2.1的熱銷,很多人開始詢問新的Play過濾器API。 實際上,API非常簡單:
本質上,過濾器只是一個執行一個動作并返回另一個動作的函數。 過濾器通常會執行的操作是包裝操作,并將其作為委托調用。 要將過濾器添加到應用程序中,只需將其添加到Global doFilter方法中即可。 我們提供了一個幫助類來幫助您:
object Global extends WithFilters(MyFilter) {... }容易吧? 包裝動作,在全局中注冊。 好吧,這很容易,但前提是您了解Play架構。 這非常重要,因為一旦您了解了Play的體系結構,便可以使用Play進行更多的工作。 我們這里有一些文檔,從較高的層次解釋了Play的體系結構。 在這篇博客文章中,我將在過濾器的上下文中解釋Play的體系結構,并附帶代碼片段和用例。
Plays架構簡介
我不需要在這里進行深入介紹,因為我已經提供了指向我們的體系結構文檔的鏈接,但是簡而言之,Play的體系結構與HTTP請求的流程非常匹配。 發出HTTP請求時到達的第一件事是請求標頭。 因此,Play中的動作必須是接受請求標頭的函數。 HTTP請求中接下來會發生什么? 身體被接收。 因此,接收請求的函數必須返回消耗主體的東西。 這是一個迭代器,它是一個React式流處理程序,在使用流后最終產生單個結果。 您不必為了了解過濾器而需要了解有關迭代操作方式的詳細信息,需要了解的重要一點是,迭代最終會產生一個結果,您可以像使用將來的map功能那樣進行map 。 有關編寫迭代對象的詳細信息,請閱讀我的博客文章 。 HTTP請求中發生的下一件事是必須發送http響應。 那么iteratee的結果是什么? HTTP響應。 HTTP響應是一組響應標頭,后跟一個響應正文。 響應主體是一個枚舉器,它是一個React流生成器。 所有這些都體現在Plays EssentialAction特性中:
trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result])這表明基本動作是一個函數,該函數采用請求標頭并返回迭代器,該迭代器消耗字節數組主體塊并最終產生結果。
更簡單的方法
在繼續之前,我想指出Play提供了一個名為Filter的輔助特性,它比使用EssentialFilter時使編寫過濾器更容易。 這類似于Action特質,因為Action無需擔心迭代和如何解析主體,從而簡化了編寫EssentialAction的過程,而只是提供了一個函數,該函數接受具有解析主體的請求,并返回結果。 Filter特質以類似的方式簡化了事情,但是我將一直講到最后,因為我認為最好在開始使用helper類之前先了解過濾器的工作原理。
Noop過濾器
為了演示過濾器的外觀,我將展示的第一件事是noop過濾器:
class NoopFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {next(request)}} }每次執行過濾器時,我們都會創建一個包裝它的新EssentialAction 。 由于EssentialAction只是一個函數,我們可以通過傳遞傳入的請求來調用它。 因此,以上是我們實現EssentialFilter基本模式。
處理請求頭
假設我們要查看請求標頭,然后根據我們檢查的內容有條件地調用包裝的操作。 可以做到這一點的過濾器示例可能是網站/admin區域的全面安全策略。 可能看起來像這樣:
class AdminFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {if (request.path.startsWith('/admin') && request.session.get('user').isEmpty) {Iteratee.ignore[Array[Byte]].map(_ => Results.Forbidden())} else {next(request)}}} }您可以在這里看到,由于我們是在解析正文之前攔截動作,因此在阻止動作時我們仍然需要提供一個正文解析器。 在這種情況下,我們將返回一個將僅忽略整個主體的主體解析器,并將其映射為禁止的結果。
處理身體
在某些情況下,您可能想對過濾器中的主體進行處理。 在某些情況下,您可能想解析正文。 如果是這種情況,請考慮改用動作合成 ,因為這樣可以在動作解析正文之后掛接動作處理。 如果要在過濾器級別解析主體,則必須對其進行緩沖,解析,然后再次對其進行流傳輸以使動作再次解析。 但是,有些事情可以在過濾器級別輕松完成。 一個示例是gzip解壓縮。 Play框架已經提供了開箱即用的gzip解壓縮功能,但是如果不是這樣,則可能是這樣(使用我的play extra iteratees項目中的gunzip枚舉):
class GunzipFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {if (request.headers.get('Content-Encoding').exists(_ == 'gzip')) {Gzip.gunzip() &>> next(request)} else {next(request)}}} }在這里,我們使用iteratee組成將人體分析器iteratee包裹在gunzip枚舉對象中。
處理響應頭
當您進行過濾時,您通常會希望對正在發送的響應進行處理。 如果您只想添加標題,或向會話中添加內容,或對響應進行任何寫操作,而無需實際讀取它,那么這很簡單。 例如,假設您要向每個響應添加一個自定義標頭:
class SosFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {next(request).map(result => result.withHeaders('X-Sos-Message' -> 'I'm trapped inside Play Framework please send help'))}} }使用處理身體的iteratee上的map函數,我們可以訪問該動作產生的結果,然后可以按照演示進行修改。 但是,如果您想讀取結果,則需要解開包裝。 播放結果是AsyncResult或PlainResult 。 一個AsyncResult是一個Result ,其中包含一個Future[Result] 。 它具有允許最終的PlainResult進行轉換的transform方法。 PlainResult具有標題和正文。 假設您要向每個新創建的會話添加時間戳以記錄創建時間。 可以這樣完成:
class SessionTimestampFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {def addTimestamp(result: PlainResult): Result = {val session = Session.decodeFromCookie(Cookies(result.header.headers.get(HeaderNames.COOKIE)).get(Session.COOKIE_NAME))if (!session.isEmpty) {result.withSession(session + ('timestamp' -> System.currentTimeMillis.toString))} else {result}}next(request).map {case plain: PlainResult => addTimestamp(plain)case async: AsyncResult => async.transform(addTimestamp)}}} }處理響應主體
您可能要做的最后一件事是轉換響應主體。 PlainResult有兩個實現, SimpleResult (用于沒有傳輸編碼的主體)和ChunkedResult (用于分塊傳輸編碼的主體)。 SimpleResult包含一個枚舉器,而ChunkedResult包含一個接受迭代器以將結果寫出的函數。 您可能要執行的操作示例是實現gzip過濾器。 一個非常幼稚的實現(例如,不要使用它,而是使用我的play iteratees項目中的完整實現),如下所示:
class GzipFilter extends EssentialFilter {def apply(next: EssentialAction) = new EssentialAction {def apply(request: RequestHeader) = {def gzipResult(result: PlainResult): Result = result match {case simple @ SimpleResult(header, content) => SimpleResult(header.copy(headers = (header.headers - 'Content-Length') + ('Content-Encoding' -> 'gzip')), content &> Enumeratee.map(a => simple.writeable.transform(a)) &> Gzip.gzip())}next(request).map {case plain: PlainResult => gzipResult(plain)case async: AsyncResult => async.transform(gzipResult)}}} }使用更簡單的API
現在,您已經了解了如何使用基本的EssentialFilter API來實現所有目標,并希望因此了解了過濾器如何適應Play的體系結構以及如何利用它們來滿足您的要求。 現在讓我們看一下更簡單的API:
trait Filter extends EssentialFilter {def apply(f: RequestHeader => Result)(rh: RequestHeader): Resultdef apply(next: EssentialAction): EssentialAction = {...} }object Filter {def apply(filter: (RequestHeader => Result, RequestHeader) => Result): Filter = new Filter {def apply(f: RequestHeader => Result)(rh: RequestHeader): Result = filter(f,rh)} }簡而言之,該API允許您編寫過濾器而不必擔心正文解析器。 它使動作看起來像是結果的請求標頭的功能。 這限制了過濾器的全部功能,但是對于許多用例而言,您根本不需要此功能,因此使用此API提供了一種簡單的替代方法。 為了演示,noop過濾器類如下所示:
class NoopFilter extends Filter {def apply(f: (RequestHeader) => Result)(rh: RequestHeader) = {f(rh)} }或者,使用“ Filter伴隨對象:
val noopFilter = Filter { (next, req) =>next(req) }請求計時過濾器可能如下所示:
val timingFilter = Filter { (next, req) =>val start = System.currentTimeMillisdef logTime(result: PlainResult): Result = {Logger.info('Request took ' + (System.currentTimeMillis - start))result}next(req) match {case plain: PlainResult => logTime(plain)case async: AsyncResult => async.transform(logTime)} }參考: James and Beth Roper博客博客中的JCG合作伙伴 James Roper 了解了Play Filter API 。
翻譯自: https://www.javacodegeeks.com/2013/02/understanding-the-play-filter-api.html
api過濾器
總結
以上是生活随笔為你收集整理的api过滤器_了解播放过滤器API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java命令行界面(第4部分):命令行
- 下一篇: jndi ldap_什么是JNDI,SP