介紹
本文的目標是通過一個用戶登錄示例,簡要的介紹使用Play進行Web開發的基本流程。本文并不會手把手教你如何創建一個Play應用,而是通過核心的代碼片段傳遞Play的一些設計理念,為不熟悉Play框架的同學提供一個快速了解的途徑。
創建登錄Controller
在controllers
目錄下創建ApplicationController
類:
package controllers
import play.api.mvc._
class ApplicationController extends Controller {
def login = Action {
Ok(views.html.login("用戶登錄"))
}
def doLogin(userName: String, password: String) = Action {
val mess = userName + "&" + password
Ok(mess)
}
}
上面定義了login
和doLogin
兩個Action,一個用于引導用戶至登錄頁面,另一個用戶處理登錄請求。一個Action其實就是一個函數,接受一個request作為參數,返回一個Result,返回的Result最終會被以Http響應的形式寫回給瀏覽器。Ok(mess)
返回的結果就是Result類型。
不熟悉Scala的同學看上面的代碼會感覺比較奇怪,
Action{...}
和Ok(...)
是什么鬼?其實這是調用單例對象上apply
方法的簡寫形式,即Action{...}
等價于Action.apply(...)
,Ok(...)
等價于Ok.apply(...)
。省略掉.apply
是不是看起來感覺舒服一點_。
另外Scala不建議使用return
語句,默認最后一條語句的值作為函數的返回值。
創建登錄View
在views
目錄下創建login.scala.html
:
@(title: String)
<!DOCTYPE html>
<html lang="en">
<head><title>@title</title></head>
<body>
<form action="/doLogin">
<input name="userName" type="text" placeholder="User Name" />
<input name="password" type="password" placeholder="Password" />
<button type="submit">立即登錄</button>
</form>
</body>
</html>
我們知道模板頁面中有兩部分內容,一部分是不可變的Html內容,另一部分是需要動態執行的代碼。而神奇的@
符號就是要告訴Play,它后面跟著的是需要動態執行的代碼。在Play中,一個模板文件就是一個函數,接受一組參數,返回動態執行后的Html內容,函數名就是不帶后綴的文件名,例如上面定義的模板文件編譯后生成的函數名稱是login
。模板文件的第一行用于指明函數的參數列表,上面的模板文件相當于定義了一個login(title: String)
函數。
把View抽象成函數好處還是很多的,例如組合多個View變成了函數之間的相互調用,另外我們也可以使用多線程加速大頁面的渲染。
Play的模板層采用Scala
語言編寫,借助Scala語言,在Play的模板層你會感覺自己像是一只脫了韁的野馬。其實在模板層只需要了解Scala的if
和for
語法即可。Scala雖然入門門檻較高,但是帶來的收益是巨大的,隨著你對Play了解的深入一定可以慢慢的體會到這點。
關聯Http請求和Action
Play使用routes文件定義Http請求和Action之間的映射關系,編輯conf/routes
文件,添加一行:
GET /login controllers.ApplicationController.login
GET /doLogin controllers.ApplicationController.doLogin(userName: String, password: String)
啟動看看效果
進入命令行,執行activator run
,在瀏覽器中打開http://localhost:9000/login
:


加上數據驗證
通常登錄操作使用Post請求,所以我們調整一下routes:
POST /doLogin controllers.ApplicationController.doLogin
ApplicationController
代碼調整如下:
package controllers
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
class ApplicationController extends Controller {
def login = Action {
Ok(views.html.login("用戶登錄"))
}
def doLogin = Action { implicit request =>
val loginForm = Form(
tuple(
"userName" -> email,
"password" -> text(minLength = 6)
)
)
loginForm.bindFromRequest().fold(
errorForm => Ok(errorForm.errors.toString()),
tupleData => {
val (userName, password) = tupleData
Ok(userName + "&" + password)
}
)
}
}
上面的fold
函數用于處理表單參數驗證通過和不通過兩種情況。
再來看看效果


小結
通過上面簡單的登錄示例我們會發現,Play中Controller和View是兩個獨立的模塊,之間沒有任何耦合。Controller完成一些業務運算,然后將數據以參數的形式傳遞給View,View沒有任何的內置對象,所有的依賴都定義在參數列表中,Controller和View之間只是簡單的函數調用關系,狀態通過函數參數進行傳遞,這樣的好處是程序執行流程容易追蹤,代碼容易閱讀,并且單元測試會變得非常簡單,當然最大的好處是多線程環境下代碼無需同步,極大地提高了執行效率。另外Play在改動代碼后無需重啟,直接刷新瀏覽器就可以了,開發體驗還是不錯的。