了解播放过滤器API

随着Play 2.1的热销,很多人开始询问新的Play过滤器API。 实际上,API非常简单:

trait EssentialFilter {def apply(next: EssentialAction): EssentialAction
}

本质上,过滤器只是一个执行一个动作并返回另一个动作的函数。 过滤器通常会执行的操作是包装操作,并将其作为委托进行调用。 要将过滤器添加到应用程序中,只需将其添加到Global doFilter方法中即可。 我们提供了一个帮助类来帮助您:

object Global extends WithFilters(MyFilter) {...
}

容易吧? 包装动作,在全局中进行注册。 好吧,这很容易,但前提是您了解Play架构。 这非常重要,因为一旦您了解了Play的体系结构,就可以使用Play进行更多的工作。 我们这里有一些文档,从较高的层次解释了Play的体系结构。 在这篇博客中,我将在过滤器的上下文中解释Play的体系结构,并附带代码片段和用例。

Plays架构简介

我不需要在这里进行深入介绍,因为我已经提供了指向我们的体系结构文档的链接,但是总而言之,Play的体系结构非常适合HTTP请求的流程。 发出HTTP请求时到达的第一件事是请求标头。 因此,Play中的动作必须是接受请求标头的函数。 HTTP请求中接下来会发生什么? 身体被接收。 因此,接收请求的函数必须返回消耗主体的东西。 这是一个迭代器,它是一个反应式流处理程序,在使用流后最终产生单个结果。 您不必为了了解过滤器而需要了解有关迭代操作方式的详细信息,需要了解的重要一点是,迭代最终会产生结果,您可以像使用将来那样使用其map函数进行map 。 有关编写迭代对象的详细信息,请阅读我的博客文章 。 HTTP请求中发生的下一件事是必须发送http响应。 那么iteratee的结果是什么? HTTP响应。 HTTP响应是一组响应标头,后跟一个响应正文。 响应主体是一个枚举器,它是一个反应流生成器。 所有这些都是在Plays EssentialAction特性中捕获的:

trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result])

这表明基本动作是一个函数,该函数采用请求标头并返回迭代器,该迭代器消耗字节数组主体块并最终产生结果。

更简单的方法

在继续之前,我想指出Play提供了一个名为Filter的辅助特性,它比使用EssentialFilter时使编写过滤器更容易。 这类似于Action特质,因为Action无需担心迭代和如何解析主体,从而简化了编写EssentialAction的过程,而只是提供了一个函数,该函数接受具有解析主体的请求,并返回结果。 Filter特质以类似的方式简化了事情,但是我将一直讲到最后,因为我认为最好在开始使用助手类之前先了解过滤器的工作原理。

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函数,我们可以访问该动作产生的结果,然后可以按照演示进行修改。 但是,如果您想读取结果,则需要解开包装。 播放结果是AsyncResultPlainResult 。 一个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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis 使用merge into

前一篇博客&#xff0c;oracle的merge into语法 &#xff1a; oracle merge into语法 mybatis 使用merge into&#xff0c;跟一般的update写法相同&#xff1a; <update id"mergeinfo">merge into user_type ausing ( select #{name} as name, #{type} as type…

php getbyid,ThinkPHP查询中的魔术方法简述

我们在使用thinkphp开发的时候&#xff0c;有时候会用到getById(1)这个方法快速的获取一条信息的内容&#xff0c;这个方法比用where(" id 1 ")->find()好用多了&#xff0c;同时查询效率也比find快速。很多人在刚开始接触这个方法的时候&#xff0c;没有多留意它…

DIV固定宽度和动态拉伸混合水平排列

1.效果图 2.源代码 html <h2>1.头部固定&#xff0c;尾部拉伸</h2> <div class"container" id"div1"><div class"head"></div><div class"tail"></div> </div><h2>2.尾部固定…

bzoj1941 [Sdoi2010]Hide and Seek

Description 小猪iPig在PKU刚上完了无聊的猪性代数课&#xff0c;天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞&#xff0c;为了消除寂寞感&#xff0c;他决定和他的好朋友giPi&#xff08;鸡皮&#xff09;玩一个更加寂寞的游戏—捉迷藏。 但是&#xff0c;他们觉得…

ubuntu修改ssh服务的端口号

一、找到ssh配置文件位置 vim /etc/ssh/sshd_config 二、修改ssh登录端口号 修改 port 22 为 port xxxx 三、重启ssh服务 /etc/init.d/ssh restart转载于:https://www.cnblogs.com/javafucker/p/8521316.html

使用CSS设置JavaFX饼图样式

渲染图表时&#xff0c; JavaFX默认提供某些颜色。 但是&#xff0c;在某些情况下&#xff0c;您想自定义这些颜色。 在此博客文章中&#xff0c;我将使用一个示例来更改JavaFX饼图的颜色&#xff0c;该示例打算在今天下午在RMOUG Training Days 2013的演示中包括。一些基于Jav…

python列表去重效率,你应该知道的python列表去重方法

前言列表去重是写Python脚本时常遇问题&#xff0c;因为不管源数据来自哪里&#xff0c;当我们转换成列表的方式时&#xff0c;有可能预期的结果不是我们最终的结果&#xff0c;最常见的就是列表中元素有重复&#xff0c;这时候第一件事我们就要做去重处理。我们先来个最简单的…

java 错误: 找不到或无法加载主类

这个问题应该很常见的&#xff0c;笔者经常手工编译一些测试代码或者小工具&#xff0c;经常用到 javac和java来编译并运行一些简单的小工具。 以Hello World来测试。 HelloWorld.java public class HelloWorld{public static void main(String[]args){System.out.println(&quo…

HTML 表单 存为EXCEL文件时 中文显示乱码

在做宣传品发放系统时&#xff0c;需求要把数据库查询的记录生成表单并转存excel文件。 在转存的EXCEL文件中文显示乱码&#xff0c;表格和其他字符正常&#xff0c;检查后发现是创建EXCEL文件打开模式不对 之前&#xff1a; myfile fs.CreateTextFile(filename,true) 之后…

在Visual Studio Code中配置GO开发环境

一、GO语言安装 详情查看&#xff1a;GO语言下载、安装、配置 二、GoLang插件介绍 对于Visual Studio Code开发工具&#xff0c;有一款优秀的GoLang插件&#xff0c;它的主页为&#xff1a;https://github.com/microsoft/vscode-go 这款插件的特性包括&#xff1a; Colorizatio…

最受欢迎的应用服务器

这是本系列的第二篇文章&#xff0c;我们将发布有关Java安装的统计数据。 使用的数据集来自免费的Plumbr安装&#xff0c;在过去六个月中&#xff0c;我们总共收集了1,024个不同的环境。 本系列的第一篇文章分析了基础-运行JVM的操作系统&#xff0c;是32位还是62位基础架构以…

SON_EXAM考试php,通用全国少儿英语等级考试:三星笔试真题

单项选择&#xff1a;36. exciting the game was! I enjoyed every minute of it.A.What B. How C.What an B.How an37.You stay here if youve finished your work.A.neednt B.mustnt C. shouldnt D.cant38 Nanjing Road in Shanghai is always crowded peole.A.with B.by c.o…

JS一些碎知识点

一些js基本知识点 Doctype 浏览器渲染模式 渲染模式发展历史在多年以前&#xff08;IE6诞生以前&#xff09;&#xff0c;各浏览器都处于各自比较封闭的发展中&#xff08;基本没有兼容性可谈&#xff09;。随着WEB的发展&#xff0c;兼容性问题的解决越来越显得迫切&#xff0…

CSS基础知识(display和visibility、overflow、文档流)

9、显示与隐藏 u display属性&#xff1a; (1)none&#xff1a;隐藏元素&#xff0c;不会再占有页面的任何空间&#xff0c;即不会影响布局。 (2)inline&#xff1a;默认值。将元素[显示]为内联元素 &#xff08;与HTML元素本身无关系&#xff09; (3)block&#xff1a…

浏览器滚动条样式更改

/* webkit内核浏览器 */::-webkit-scrollbar { width: 8px; }::-webkit-scrollbar-button { width: 8px; height: 5px; }::-webkit-scrollbar-track { background-color: #ddd; border-radius: 0px; }::-webkit-scrollbar-thumb { background: #999; border-radius: 0px; }::-w…

最受欢迎的Java环境

该职位将是即将发布的系列文章中的第一篇。 我们从所使用的环境开始&#xff1a;如果您感兴趣的是最受欢迎的JVM供应商或JVM版本&#xff0c;那么32bit是比64bit更流行的体系结构&#xff0c;还是Windows 8比Windows XP更流行的体系结构-这些都将在我们的文章中介绍。 在下一个…

爬空气质量MySQL,mysql

Flask 定了2中上下文&#xff0c;来实现机遇线程协程的&#xff0c;wsgi服务的请求(request、session)和存储(g&#xff0c;current_app )过程&#xff0c;通过栈来完成不同线程和协程的上下文切换&#xff0c;在与celery相结合处理异步任务时&#xff0c;需要保证异步任务在同…

使用宏实现透视表部分功能,将AB列数据合并统计.

功能:1.筛选B列;2.将A列中的值按照筛选后的结果进行合计. 这个特殊点是按照月日进行筛选的. Sub count_a() Dim sh As Worksheet Set sh ActiveSheet Range("C2:D" & [D65536].End(3).Row).Clear For line_b 2 To [B65536].End(3).Row If Len(Cells(line_b, &q…

HTML基础知识(常见元素、列表、链接元素、图片元素)

1、HTML有关概念 全称: Hyper Text Markup Language&#xff08;超文本标记语言&#xff09; 其文件扩展名为“.html”或“.htm” * 超文本 - 在普通的文本基础上&#xff0c;添加超链接、图片、音频或视频等 * 标记 - 标记就是HTML中的标签(元素)&#xff0c;特点:<a> …

使用Google Guava的订购API

我们一直在使用Google的Guava库做更多的事情-多么出色的库&#xff01; 我们用于它的最新内容是为我们的域对象整理比较器。 就是这样。 使用Apache Isis的JDO Objectstore &#xff0c;使您的类实现java.lang.Comparable &#xff0c;并对集合使用SortedSet是一个好习惯。 您可…