文档章节

Action composition【翻译】

我是菜鸟我骄傲
 我是菜鸟我骄傲
发布于 2017/04/23 09:09
字数 1669
阅读 18
收藏 0

原文:Action composition

这章将介绍几种定义常用Action函数的方式。

自定义Action构建器

我们在前面看到过,有几种声明Action的方式——使用Request参数,不使用request参数,使用Body解析器等等。事实上,正如我们将在异步编程章节看到的. 还有更多的方式。

实际上,这些构建Action的方法都是通过一个叫 ActionBuilder 的特质定义的,并且我们用来声明我的Action的Action对象只是这个特质的实例。通过实现你自己的ActionBuilder, 你可以声明一个可被重复使用的Action栈,然后这可以被用来构建Action。

让我们从一个简单的日志装饰的例子开始,我想记录每次对这个Action的调用。第一种方式是在invokeBlock 方法中实现这个功能,这个方法通过ActionBuilder构建每个Action时调用:

import play.api.mvc._

object LoggingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Logger.info("Calling action")
block(request)
}
}

现在我们可以用相同的方式使用 Action:

def index = LoggingAction {
Ok("Hello World")
}

由于 ActionBuilder 提供了构建Action的所有不同的方法,这也适用于,如声明一个自定义的Body解析器:

def submit = LoggingAction(parse.text) { request =>
Ok("Got a body " + request.body.length + " bytes long")
}

组成Action

在大多数应用里,我想要有多种Action的构建器,一些做不同类型的验证,一些提供不同类型的通用功能等等。这样的话,我们不想为每一个Action构建器类型重写我们的日志Action代码,我们想把它定义成一个可以服用的方式。

可复用Action代码可以通过封装Action被实现:

import play.api.mvc._

case class Logging[A](action: Action[A]) extends Action[A] {

def apply(request: Request[A]): Future[Result] = {
Logger.info("Calling action")
action(request)
}

lazy val parser = action.parser
}

我们也可以使用Action 构建器来构建Action而不用定义我们自己的Action类:

import play.api.mvc._

def logging[A](action: Action[A])= Action.async(action.parser) { request =>
Logger.info("Calling action")
action(request)
}

可以使用composeAction 方法把Action混合进Action构建器:

object LoggingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
override def composeAction[A](action: Action[A]) = new Logging(action)
}

现在构建者可以像前面那样被使用:

def index = LoggingAction {
Ok("Hello World")
}

我们也可以不使用Action构建器混入封装Action:

def index = Logging {
Action {
Ok("Hello World")
}
}

更复杂的Action

到目前为止,我们已经展示了根本不会影响请求的Action。当然,我们也可以读和修改传入的请求对象:

import play.api.mvc._

def xForwardedFor[A](action: Action[A]) = Action.async(action.parser) { request =>
val newRequest = request.headers.get("X-Forwarded-For").map { xff =>
new WrappedRequest[A](request) {
override def remoteAddress = xff
}
} getOrElse request
action(newRequest)
}

注意:Play已经内置了对 X-Forwarded-For 头的支持

我们可以阻塞请求:

import play.api.mvc._

def onlyHttps[A](action: Action[A]) = Action.async(action.parser) { request =>
request.headers.get("X-Forwarded-Proto").collect {
case "https" => action(request)
} getOrElse {
Future.successful(Forbidden("Only HTTPS requests allowed"))
}
}

最后我们也可以修改返回结果:

import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._

def addUaHeader[A](action: Action[A]) = Action.async(action.parser) { request =>
action(request).map(_.withHeaders("X-UA-Compatible" -> "Chrome=1"))
}

不同的请求类型

Action组合允许你在HTTP请求和响应级别上执行额外的处理,经常你想要构建数据转换的管道来添加上下文或者执行验证到请求自身。ActionFunction 可以被认为是做为请求的方法,参数化输入请求类型和输出请求类型传递个下一层。每一个Action方法可以表示模块化处理,如验证,数据库查找对象,权限检查,或者你希望跨Action组合和复用的其他的操作。 有几个实现了ActionFunction 的预定义的特质,他们对一些不同类型的操作有用:

  • ActionTransformer 可以改变请求,例如添加额外的信息。
  • ActionFilter 可以选择性了拦截请求,例如在不改变请求值的情况下产生错误。
  • ActionRefiner 是上述两种的一般情况。
  • ActionBuilder 使用Request 做为输入的函数的特例,因此可以构建Action。

你也可以通过实现invokeBlock方法定义你自己的想要的 ActionFunction。通常很容易让输入输出类型成为Request (使用WrappedRequest)的实例。

身份验证

Action方法最常用的案例之一是身份验证。我们可以很容易的实现我们自己的从原始的请求确定用户的身份验证的Action转换器,并把它添加到新的UserRequest中。注意由于他需要一个简单的Request 做为输入,因此这也是一个ActionBuilder:

import play.api.mvc._

class UserRequest[A](val username: Option[String], request: Request[A]) extends WrappedRequest[A](request)

object UserAction extends
ActionBuilder[UserRequest] with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) = Future.successful {
new UserRequest(request.session.get("username"), request)
}
}

Play也提供了一个内置的身份验证的Action构建器。关于这个的信息和怎样使用它可以在这里找到.

注意:内置的身份验证Action构建器,只是一个在简单情况下实现身份验证的最少必要代码的便利的助手,它的实现和上面的例子类似。 如果你有比内置身份验证Action能满足的需求还要复杂的需求,那么推荐不只是简单的实现你自己的Action。

给请求增加信息

现在让我们考虑一下与Item类型的对象一起工作的REST API。在/item/:itemId 路径下也许有很多路由,他们中的每一个都要寻找Item。在这个案例中,把这个逻辑放到Action方法中也许是有用的。 首先,我们将创建一个增加了Item的请求对象到我们的UserRequest:

import play.api.mvc._

class ItemRequest[A](val item: Item, request: UserRequest[A]) extends WrappedRequest[A](request) {
def username = request.username
}

现在我们将创建一个寻找那些Item的Action细化器,并返回一个错误(Left)Either 或者一个新的 ItemRequest (Right).注意这个Action细化器被定义在一个携带Item的ID的方法内:

def ItemAction(itemId: String) = new ActionRefiner[UserRequest, ItemRequest] {
def refine[A](input: UserRequest[A]) = Future.successful {
ItemDao.findById(itemId)
.map(new ItemRequest(_, input))
.toRight(NotFound)
}
}

验证请求

最后,我们也许想要一个验证请求是应该继续的Action方法。例如,也许我们想检查一下UserAction 的中的用户是否有权限从ItemAction获取Item,如果没有返回一个错误:

object PermissionCheckAction extends ActionFilter[ItemRequest] {
def filter[A](input: ItemRequest[A]) = Future.successful {
if (!input.item.accessibleByUser(input.username))
Some(Forbidden)
else
None
}
}

所有都放在一起

现在我们可以使用andThen 把这些Action方法连接到一起(以ActionBuilder开头)来创建一个Action:

def tagItem(itemId: String, tag: String) =
(UserAction andThen ItemAction(itemId) andThen PermissionCheckAction) { request =>
request.item.addTag(tag)
Ok("User " + request.username + " tagged " + request.item.id)
}

Play也提供了一个全局过滤API ,这对全局横切关注点有用。

© 著作权归作者所有

我是菜鸟我骄傲
粉丝 13
博文 238
码字总数 150711
作品 0
西安
架构师
私信 提问
使用 cocos2d 开发 iphone 游戏入门概念

简介: Cocos2D-iPhone是 一个开源框架,利用它可以非常容易的在iPhone上开发2D游戏。它提供了物理,精灵(sprites),时差卷轴(parallax scrolling),支持iPhone touch和accelerator等很多...

鉴客
2010/06/09
3.3K
1
play2.0文档-面向java开发者(6)

Action composition Action组合 This chapter introduces several ways to define generic action functionality. 这章节介绍几个定义通用action功能的方式。 Reminder about actions actio......

老盖
2012/04/12
0
0
PlayScala 2.5.x - Filter开发指南

Filter简介 Filter是Play基于责任链模式(Chain of Responsibility)实现的过滤器,利用Filter可以过滤所有的请求和响应。Play的Filter实现非常灵活,你可以在Filter中修改请求和响应,或终止F...

joymufeng
2016/06/01
152
0
Facelets 标签参考

Facelets 标签参考 lxm翻译自《Facelets Essentials: Guide to JavaServer™ Faces View Definition Framework 》 不当之处欢迎指正。 标签在 JSF 组件树中插入一个 UIComponet 实例,并作为...

hxzon
2009/03/05
543
0
NativeScript 4.0.0 发布,创建跨平台原生应用的框架

NativeScript 4.0.0 已发布,NativeScript 可以使用 Javascript,CSS, XML 创建真正的 Native 跨平台应用,支持 iOS Android 和 Windows Universal,NativeScript 可将跨平台代码翻译成目标平...

周其
2018/04/14
6.6K
11

没有更多内容

加载失败,请刷新页面

加载更多

web前端性能优化

js相关 根据js时间线,将js放在最下面加载,先加载DOM,再加载js 懒执行,就是将某些逻辑延迟到使用时再执行。该技术可用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒...

祖达
17分钟前
0
0
websocket通信failed to execute 'send'问题的解决

在建立web socket通信后,发送数据时,出现下图所示现象: 问题代码演示 function TestSockets() { //实例化一个WebSocket对象 var socket = new WebSocket("ws://127.0.0.1:8000/w...

城市之雾
23分钟前
0
0
PHPStorm 如何设置 yaf 代码提示?

下载 php-yaf-doc git clone https://github.com/elad-yosifon/php-yaf-doc.git PHPStorm 如下设置 使用

whoru
24分钟前
0
0
typeorm drop foreign key

1. 问题描述 项目代码中使用typeorm进行mysql的数据操作, 昨天突然部署测试环境发现测试环境数据库的外键都为空了, 导致涉及的整个系统不可用. 2. 问题探究 尝试复现 mysqldump 线上数据, 然...

jimmywa
25分钟前
2
0
好程序员web前端开发测验之css部分

好程序员web前端开发测验之css部分Front End Web Development Quiz CSS 部分问题与解答 Q: CSS 属性是否区分大小写? <p><font size="3">  ul {</font></p> <p><font size="3">  MaRGin:......

好程序员IT
30分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部