Action and view
===============
Để linh hoạt, Xitrum cung cấp 3 loại actions sau:
``Action`` thông thường, ``FutureAction``, và ``ActorAction``.
Action thông thường
-------------------
::
import xitrum.Action
import xitrum.annotation.GET
@GET("hello")
class HelloAction extends Action {
def execute() {
respondText("Hello")
}
}
Bởi vì các action sẽ chạy trực tiếp trên luồng (thread) IO của Netty nên các action không
nên tốn thời gian xử lý (block process), mặt khác nếu thời gian xử lý của thread IO của Netty
kéo dài, Netty sẽ không còn khả năng đáp ứng các yêu cầu từ phía client hoặc không thể tiếp nhận
các kết nối mới.
FutureAction
------------
::
import xitrum.FutureAction
import xitrum.annotation.GET
@GET("hello")
class HelloAction extends FutureAction {
def execute() {
respondText("hi")
}
}
FutureAction sẽ chạy trong cùng thread pool với ``ActorAction`` dưới đây, được tách
ra từ một phần của Netty thread pool.
Actor action
------------
Nếu vạn muốn action của bạn hoạt động như một Akka actor, hãy kế thừa nó từ ``ActorAction``:
::
import scala.concurrent.duration._
import xitrum.ActorAction
import xitrum.annotation.GET
@GET("actor")
class HelloAction extends ActorAction {
def execute() {
// See Akka doc about scheduler
import context.dispatcher
context.system.scheduler.scheduleOnce(3 seconds, self, System.currentTimeMillis())
// See Akka doc about "become"
context.become {
case pastTime =>
respondInlineView(s"It's $pastTime Unix ms 3s ago.")
}
}
}
Một actor instance sẽ được tạo khi có một yêu cầu (request), actor sẽ được dừng khi
đóng kết nối hoặc response được gửi bởi các method ``respondText``, ``respondView``, v.v.
Với chunked response, actor sẽ không dừng lại ngay lập tức mà dừng lại khi chunk cuối cùng
được gửi đi.
Actor này sẽ chạy trong thread pool của Akka actor có tên là "xitrum"
Gửi Respond cho client
--------------------------
Từ một action để trả về một respond cho phía client bạn có thể sử dụng những method sau:
* ``respondView``: trả về một tệp view , có hoặc không có layout
* ``respondInlineView``: trả về một được nhúng (không phải một tệp riêng lẻ), có hoặc không có layout
* ``respondText("hello")``: trả về một chuỗi ký tự không có layout
* ``respondHtml("...")``: như trên, với content type là "text/html"
* ``respondJson(List(1, 2, 3))``: chuyển đối tượng (object) Scala thành đối tượng JSON và trả về client.
* ``respondJs("myFunction([1, 2, 3])")``
* ``respondJsonP(List(1, 2, 3), "myFunction")``: kết hợp của 2 loại trên.
* ``respondJsonText("[1, 2, 3]")``
* ``respondJsonPText("[1, 2, 3]", "myFunction")``
* ``respondBinary``: trả về một mảng byte
* ``respondFile``: gửi file trực tiếp từ đĩa một cách nhanh chóng bằng kỹ thuật `zero-copy
Hello {s}!
) } } Render fragment --------------- Giả sử tệp MyAction.jade có đường dẫn: scr/main/scalate/mypackage/MyAction.jade Nếu bạn muốn tạo tệp fragment trong cùng thư mục: scr/main/scalate/mypackage/_MyFragment.jade :: renderFragment[MyAction]("MyFragment") Nếu ``MyAction`` là current action, bạn có thể bỏ qua: :: renderFragment("MyFragment") Trả về view cho action khác ---------------------------- Sử dụng cú pháp ``respondView[ClassName]()``: :: package mypackage import xitrum.Action import xitrum.annotation.{GET, POST} @GET("login") class LoginFormAction extends Action { def execute() { // Respond scr/main/scalate/mypackage/LoginFormAction.jade respondView() } } @POST("login") class DoLoginAction extends Action { def execute() { val authenticated = ... if (authenticated) redirectTo[HomeAction]() else // Reuse the view of LoginFormAction respondView[LoginFormAction]() } } Một action - nhiều view ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nếu bạn muốn có nhiều view cho một action: :: package mypackage import xitrum.Action import xitrum.annotation.GET // These are non-routed actions, for mapping to view files: // scr/main/scalate/mypackage/HomeAction_NormalUser.jade // scr/main/scalate/mypackage/HomeAction_Moderator.jade // scr/main/scalate/mypackage/HomeAction_Admin.jade trait HomeAction_NormalUser extends Action trait HomeAction_Moderator extends Action trait HomeAction_Admin extends Action @GET("") class HomeAction extends Action { def execute() { val userType = ... userType match { case NormalUser => respondView[HomeAction_NormalUser]() case Moderator => respondView[HomeAction_Moderator]() case Admin => respondView[HomeAction_Admin]() } } } Sử dụng các non-routed action như trên khá phức tạp, nhưng đó là cách typesafe. Bạn cũng có thể sử dụng ``String``để chỉ ra đường dẫn đến : :: respondView("mypackage/HomeAction_NormalUser") respondView("mypackage/HomeAction_Moderator") respondView("mypackage/HomeAction_Admin") Component --------- Bạn có thể tạo và tái sử dụng các component của view. Về cơ bản, một component gần giống với một action và có các tính chất sau: * Component không có route, do đó không cần đến method ``execute``. * Component không trả về một respond hoàn chỉnh, Component chỉ ``render`` ra các fragment của view. Do đó trong một component, thay vì sử dụng ``repondXXX``, bạn hãy sử dụng ``renderXXX``. * Giống với một action, một component có thể không có, có một, hoặc có nhiều view liên kết với nhau. :: package mypackage import xitrum.{FutureAction, Component} import xitrum.annotation.GET class CompoWithView extends Component { def render() = { // Render associated view , e.g. CompoWithView.jade // Note that this is renderView, not respondView! renderView() } } class CompoWithoutView extends Component { def render() = { "Hello World" } } @GET("foo/bar") class MyAction extends FutureAction { def execute() { respondView() } } MyAction.jade: :: - import mypackage._ != newComponent[CompoWithView]().render() != newComponent[CompoWithoutView]().render()