首页 > 代码库 > Xitrum学习笔记03 - Action和View

Xitrum学习笔记03 - Action和View

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=http://www.mamicode.com/{url}) Path to the current action"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=http://www.mamicode.com/{url}) Path to the current action"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")

Xitrum学习笔记03 - Action和View