Action:
Xitrum 提供了3种Action:普通Action, FutureAction 和 ActorAction
它们都是Trait,而不是 Class
1. 普通Action:
当请求到来时,Action实现类直接在Netty的 IO线程上运行,不能用普通Action来执行 消耗很长时间的处理,否则Netty就不能接收新的连接或发出响应到客户端
import xitrum.Action import xitrum.annotation.GET @GET("hello") class HelloAction extends Action { def execute() { respondText("Hello") } }
2. FutureAction:
扩展了 Action,FutureAction实现类异步运行在和ActorAction相同的线程池上,这个线程池和Netty线程池是分离的
FutureAction实现类的执行context是 xitrum.Config.actorSystem.dispatcher.
trait FutureAction extends Action
import xitrum.FutureAction import xitrum.annotation.GET @GET("hello") class HelloAction extends FutureAction { def execute() { respondText("hi") } }
3. ActorAction:
trait ActorAction extends Actor with Action
实现类是一个 Akka actor。
当有需求时,一个actor实例被创建。当连接关闭时,或者响应通过 respondText/respondView 等方法被发出时,actor会被停止。
对于分块的响应,actor会在最后一个块儿的响应发出后才被停止。
actor运行在命名为"xiturm"的Akka actor系统的线程池中。
1 import scala.concurrent.duration._ 2 import xitrum.ActorAction 3 import xitrum.annotation.GET 4 @GET("actor") 5 class HelloAction extends ActorAction { 6 def execute() { 7 // See Akka doc about scheduler 8 import context.dispatcher 9 context.system.scheduler.scheduleOnce(3 seconds, self, System.currentTimeMillis()) 10 // See Akka doc about "become" 11 context.become { 12 case pastTime => 13 respondInlineView(s"It‘s $pastTime Unix ms 3s ago.") 14 } 15 } 16 }
响应(Response)方式:
1. 对于action来说,要向客户端发出响应,可以调用如下方法
? respondView: responds view template file, with or without layout
? respondInlineView: responds embedded template (not separate template file), with or without layout
? respondText("hello"): responds a string without layout
? respondHtml("<html>...</html>"): same as above, with content type set to “text/html”
? respondJson(List(1, 2, 3)): converts Scala object to JSON object then responds
? respondJs("myFunction([1, 2, 3])")
? respondJsonP(List(1, 2, 3), "myFunction"): combination of the above two
? respondJsonText("[1, 2, 3]")
? respondJsonPText("[1, 2, 3]", "myFunction")
? respondBinary: responds an array of bytes
? respondFile: sends a file directly from disk, very fast because zero-copy (aka send-file) is used
? respondEventSource("data", "event")
2. 响应返回事先创建好的View模板文件内容
要运用这种方式,每个Action scala文件都要对应一个View jade文件。代码示例如下:
scr/main/scala/mypackage/MyAction.scala:
package mypackage import xitrum.Action import xitrum.annotation.GET @GET("myAction") class MyAction extends Action { def execute() { respondView() } def hello(what: String) = "Hello %s".format(what) }
scr/main/scalate/mypackage/MyAction.jade:
- import mypackage.MyAction !!! 5 html head != antiCsrfMeta != xitrumCss //包含了Xitrum默认的CSS。这个CSS是可以移除的 != jsDefaults //包含了 jQuery、jQuery验证插件等等内容,要放在<head>标签中 title Welcome to Xitrum body a(href={url}) Path to the current action p= currentAction.asInstanceOf[MyAction].hello("World") //将currentAction转换为MyAction,并调用其hello方法 != jsForView //包含了由jsAddToView标签添加的JavaScript代码,jsForView标签应该放在整个layout的底部
在View模板中,可以使用 xitrum.Action中定义的所有方法,而且也可以使用由Scalate提供的一些utility方法(参考 Scalate doc)
默认的 Scalate 模板类型是 Jade,其他的有 Mustache, Scaml 和 Ssp。在config/xitrum.conf文件中配置默认模板类型.
# Comment out if you don‘t use template engine. template { "xitrum.view.Scalate" { defaultType = jade # jade, mustache, scaml, or ssp } }
也可以在Action实现类调用 respondView时指定模板类型
val options = Map("type" ->"mustache") respondView(options)
如果需要在View中多次调用Action实现类的的方法的话,只需要将currentAction做一次转换,如:
- val myAction = currentAction.asInstanceOf[MyAction]; import myAction._ p= hello("World") p= hello("Scala") p= hello("Xitrum")
Layout
利用respondView或respondInlineView提交一个View时,Xitrum会把View内容表示成一个String,然后把这个String设置给renderedView变量。
Xitrum调用当前Action的layout方法,把方法的结果返回给浏览器。
默认的layout方法只返回 renderedView 本身,可以通过重写layout方法以添加关于view的其他内容。如果重写的layout方法中包含renderedView,它只是作为layout的一部分内容。
layout方法在action的view生成之后才被调用,layout方法返回的内容就是要响应到浏览器中的内容。
可以通过创建一个继承自Action的Trait,并将其相应的View模板文件定义为通用layout。再让其他Action scala实现这个Trait,这样其他Action的页面就会包含这个通用的layout。
示例:
src/main/scala/mypackage/AppAction.scala
package mypackage import xitrum.Action trait AppAction extends Action { override def layout = renderViewNoLayout[AppAction]() }
src/main/scalate/mypackage/AppAction.jade
!!! 5 html head != antiCsrfMeta != xitrumCss != jsDefaults title Welcome to Xitrum body != renderedView != jsForView
src/main/scala/mypackage/MyAction.scala
package mypackage import xitrum.annotation.GET @GET("myAction") class MyAction extends AppAction { def execute() { respondView() } def hello(what: String) = "Hello %s".format(what) }
scr/main/scalate/mypackage/MyAction.jade:
- import mypackage.MyAction a(href={url}) Path to the current action p= currentAction.asInstanceOf[MyAction].hello("World")
此示例有待验证
layout不在View模板里的示例(直接写到 Action Scala文件里)
示例1:
import xitrum.Action import xitrum.view.DocType trait AppAction extends Action { override def layout = DocType.html5( <html> <head> {antiCsrfMeta} {xitrumCss} {jsDefaults} <title>Welcome to Xitrum</title> </head> <body> {renderedView} {jsForView} </body> </html> ) }
示例2:
val specialLayout = () => DocType.html5( <html> <head> {antiCsrfMeta} {xitrumCss} {jsDefaults} <title>Welcome to Xitrum</title> </head> <body> {renderedView} {jsForView} </body> </html> ) respondView(specialLayout _)
Inline view
import xitrum.Action import xitrum.annotation.GET
@GET("myAction")
class MyAction extends Action {
def execute() {
val s = "World" // Will be automatically HTML-escaped
respondInlineView(
<p>Hello <em>{s}</em>!</p>
)
}
}
Render fragment
有两个Jade View文件:
scr/main/scalate/mypackage/MyAction.jade,scr/main/scalate/mypackage/_MyFragment.jade
如果想提交fragment文件到相同路径下的其他Jade文件里,可以使用
renderFragment[MyAction]("MyFragment")
如果在这种情况下,MyAction是当前action,则上面的代码可以写成 renderFragment("MyFragment")