Play框架背后的家伙一直在努力開發新版本的Play 2.0。 在Play 2.0中,scala扮演著更為重要的角色,尤其是整個構建過程得到了極大的改進。 到目前為止,Play 2.0遇到的唯一問題是缺少好的文檔。 你們在努力更新Wiki方面很努力,但是要獲得所需的內容,仍然經常需要反復試驗。 但是請注意,這通常不只是由Play引起的,我有時仍然會遇到更奇特的Scala構造;-)
在本文中,我將向您介紹如何使用Scala在Play 2.0中完成一些常見任務。 更具體地說,我將向您展示如何創建一個應用程序:
- 使用基于sbt的依賴性管理來配置外部依賴性
- 使用play eclipsify命令在Eclipse中(帶有Scala-ide插件)進行編輯
- 使用Play的路線提供Rest API
- 使用Akka 2.0(由Play框架提供)異步調用數據庫并生成Json(正因為我們可以)
- 使用Play提供的Json功能(基于jerkson)將Scala對象轉換為Json
我不會讓使用牢騷的數據庫訪問,如果您想了解更多關于看看這個文章。 我想將Querulous代碼轉換為使用Anorm。 但是由于我最后一次與Anorm的經歷是,我該如何說服我,而不是令人信服的積極,所以將其保存下來供以后使用。
使用Play 2.0創建應用程序
使用Play 2.0進行安裝和運行非常容易,并且有據可查,因此我不會在此花費太多時間。 有關完整說明,請參見Play 2.0 Wiki 。 要啟動并運行,請先下載并提取Play 2.0,然后執行以下步驟:
從控制臺執行以下命令:
$play new FirstStepsWithPlay20
這將創建一個新項目,并向您顯示以下輸出:
_ __ | | __ _ _ _| |
| '_ \| |/ _' | || |_|
| __/|_|\____|\__ (_)
|_| |__/ play! 2.0-RC2, http://www.playframework.orgThe new application will be created in /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay20What is the application name?
> FirstStepsWithPlay20Which template do you want to use for this new application? 1 - Create a simple Scala application2 - Create a simple Java application3 - Create an empty project> 1OK, application FirstStepsWithPlay20 is created.Have fun!
現在,您已經可以運行一個應用程序。 轉到剛剛創建的目錄并執行播放運行。
$ play run[info] Loading project definition from /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/project
[info] Set current project to FirstStepsWithPlay2 (in build file:/Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/)--- (Running the application from SBT, auto-reloading is enabled) ---[info] play - Listening for HTTP on port 9000...(Server started, use Ctrl+D to stop and go back to the console...)
如果導航到http:// localhost:9000 ,則可以看到第一個Play 2.0應用程序。 至此,Play 2.0的基本安裝已完成。
依賴管理
我在引言中提到我不是從頭開始這個項目的。 我將用Play 1.2.4,Akka 1.x,JAX-RS和Json-Lift開發的Rest服務重寫為Play 2.0框架提供的組件。 由于依賴管理在Play 1.2.4和Play 2.0之間發生了變化,因此我需要使用所需的依賴來配置新項目。 在Play 2.0中,您可以在名為build.scala的文件中執行此操作,您可以在項目的項目文件夾中找到該文件。 在添加上一個項目的依賴關系后,該文件如下所示:
import sbt._
import Keys._
import PlayProject._object ApplicationBuild extends Build {val appName = "FirstStepsWithPlay2"val appVersion = "1.0-SNAPSHOT"val appDependencies = Seq("com.twitter" % "querulous" % "2.6.5" ,"net.liftweb" %% "lift-json" % "2.4" ,"com.sun.jersey" % "jersey-server" % "1.4" ,"com.sun.jersey" % "jersey-core" % "1.4" , "postgresql" % "postgresql" % "9.1-901.jdbc4")val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(// Add extra resolver for the twitter resolvers += "Twitter repo" at "http://maven.twttr.com/" ,resolvers += "DevJava repo" at "http://download.java.net/maven/2/")
}
閱讀sbt文檔(http://code.google.com/p/simple-build-tool/wiki/LibraryManagement )后,如何使用此文件非常簡單。 基本上,我們使用appDependencies定義了所需的庫,并定義了一些額外的存儲庫,其中sbt應該從中下載其依賴項(使用解析器)。 值得一提的是,您可以在定義依賴項時指定%%。 這意味著我們還想搜索與我們的scala版本匹配的庫。 SBT查看我們當前配置的版本,并為該版本添加限定符。 這可以確保我們得到一個適用于我們的Scala版本的版本。
就像我提到的,我想用Play 2.0的功能替換我使用的大多數外部庫。 刪除我不再使用的內容后,該文件如下所示:
import sbt._
import Keys._
import PlayProject._object ApplicationBuild extends Build {val appName = "FirstStepsWithPlay2"val appVersion = "1.0-SNAPSHOT"val appDependencies = Seq("com.twitter" % "querulous" % "2.6.5" ,"postgresql" % "postgresql" % "9.1-901.jdbc4")val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(// Add extra resolver for the twitter resolvers += "Twitter repo" at "http://maven.twttr.com/")
}
配置依賴項后,我可以為我的IDE配置該項目。 盡管我的所有同事都是IntelliJ的擁護者,但我仍然會回到以前的習慣:Eclipse。 因此,讓我們看看您需要做些什么才能在Eclipse中啟動和運行該項目。
從Eclipse工作
在我的Eclipse版本中,我安裝了scala插件 ,而Play 2.0框架可以很好地與該插件一起使用。 要使您的項目在eclipse中運行,您所要做的就是運行以下命令:play eclipsify
jos@Joss-MacBook-Pro.local:~/dev/play-2.0-RC2/FirstStepsWithPlay2$ ../play eclipsify
[info] Loading project definition from /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/project
[info] Set current project to FirstStepsWithPlay2 (in build file:/Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/)
[info] About to create Eclipse project files for your project(s).
[info] Compiling 1 Scala source to /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay2/target/scala-2.9.1/classes...
[info] Successfully created Eclipse project files for project(s): FirstStepsWithPlay2
jos@Joss-MacBook-Pro.local:~/dev/play-2.0-RC2/FirstStepsWithPlay2$
現在,您可以使用Eclipse中的“導入項目”,并且可以直接從Eclipse中編輯Play 2.0 / Scala項目。 可以直接從Eclipse啟動Play環境,但是我還沒有使用過。 我只是從命令行啟動Play項目,一次,我在Eclipse中所做的所有更改都立即可見。 對于那些與Play玩了更長的時間的人來說,這可能不再那么特別了。 就我個人而言,我仍然對這種環境的生產力感到驚訝。
使用Play的路線提供Rest API
在我以前的Play項目中,我使用jersey模塊能夠使用JAX-RS批注指定我的Rest API。 由于Play 2.0包含許多重大的API更改,并且幾乎是從頭開始的重寫,因此您不能指望所有舊模塊都能正常工作。 Jersey模塊也是如此。 我確實深入研究了該模塊的代碼,以查看更改是否微不足道,但是由于找不到關于如何為Play 2.0創建插件以允許您與路線處理進行交互的文檔,因此我決定只切換到Play 2.0可以休息的方式。 使用“ routes”文件,可以很容易地將我只暴露給一個簡單控制器的兩個操作連接起來:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~GET /resources/rest/geo/list controllers.Application.processGetAllRequest
GET /resources/rest/geo/:id controllers.Application.processGetSingleRequest(id:String)
相應的控制器如下所示:
package controllersimport akkawebtemplate.GeoJsonService
import play.api.mvc.Action
import play.api.mvc.Controllerobject Application extends Controller {val service = new GeoJsonService()def processGetSingleRequest(code: String) = Action {val result = service.processGetSingleRequest(code)Ok(result).as("application/json")}def processGetAllRequest() = Action {val result = service.processGetAllRequest;Ok(result).as("application/json");}
}
如您所見,我剛剛創建了非常簡單的基本操作。 還沒有研究過錯誤和異常處理,但是Play提供的Rest API確實使使用附加的Rest框架變得不必要。 多數民眾贊成在第一個框架。 我的原始應用程序的下一部分需要更改的是Akka代碼。 Play 2.0包含Akka庫的最新版本(2.0-RC1)。 由于我的原始Akka代碼是針對1.2.4編寫的,因此存在許多沖突。 更新原始代碼并不是一件容易的事。
使用Akka 2.0
我不會深入探討Akka 2.0遇到的所有問題。 最大的問題是Play Wiki上的文檔非常糟糕,Akka網站上的文檔非常糟糕,而我在Akka文檔中找到正確信息的技能也很糟糕。 與我一起僅使用Akka大約三四個月,并不能使其成為最佳組合。 經過幾個小時的挫折之后,我才刪除了所有現有的Akka代碼,并從頭開始。 20分鐘后,我使用Akka 2進行了所有工作,并使用了Play的主配置。 在下一個清單中,您可以看到相應的代碼(我有意離開了導入,因為在很多示例中您都可以找到它們,因此將它們省略了,這很容易,但要困難得多)
import akka.actor.actorRef2Scala
import akka.actor.Actor
import akka.actor.Props
import akka.dispatch.Await
import akka.pattern.ask
import akka.util.duration.intToDurationInt
import akka.util.Timeout
import model.GeoRecord
import play.libs.Akka
import resources.commands.Command
import resources.commands.FULL
import resources.commands.SINGLE
import resources.Database/*** This actor is responsible for returning JSON objects from the database. It uses querulous to * query the database and parses the result into the GeoRecord class.*/
class JsonActor extends Actor {/*** Based on the type recieved we determine what command to execute, most case classes* can be executed using the normal two steps. Execute a query, convert result to* a set of json data and return this result.*/def receive = {// when we receive a Command we process it and return the resultcase some: Command => {// execute the query from the FULL command and process the results using the// processRows functionvar records:Seq[GeoRecord] = null;// if the match parameter is null we do the normal query, if not we pass in a set of varargssome.parameters match {case null => records = Database.getQueryEvaluator.select(some.query) {some.processRows}case _ => records = Database.getQueryEvaluator.select(some.query, some.parameters:_*) {some.processRows}}// return the result as a json stringsender ! some.toJson(records)}case _ => sender ! null}
}/*** Handle the specified path. This rest service delegates the functionality to a specific actor* and if the result from this actor isn't null return the result*/
class GeoJsonService {def processGetSingleRequest(code: String) = {val command = SINGLE();command.parameters = List(code);runCommand(command);}/*** Operation that handles the list REST command. This creates a command* that forwards to the actor to be executed.*/def processGetAllRequest:String = {runCommand(FULL());}/*** Function that runs a command on one of the actors and sets the response*/private def runCommand(command: Command):String = {// get the actorval actor = Akka.system.actorOf(Props[JsonActor])implicit val timeout = Timeout(5 seconds)val result = Await.result(actor ? command, timeout.duration).asInstanceOf[String]// return result as Stringresult}
}
很多代碼,但是我想向您展示actor的定義以及如何使用它們。 總結一下,使用Akka執行請求/回復模式所需的Akka 2.0代碼是這樣的:
private def runCommand(command: Command):String = {// get the actorval actor = Akka.system.actorOf(Props[JsonActor])implicit val timeout = Timeout(5 seconds)val result = Await.result(actor ? command, timeout.duration).asInstanceOf[String]// return result as Stringresult}
這使用全局Akka配置來檢索所需類型的actor。 然后,我們向演員發送命令,并返回一個Future,在其上我們等待5秒鐘的結果,然后將其轉換為String。 此未來等待我們的演員發送回復。 這是在actor本身中完成的:
sender ! some.toJson(records)
替換了Akka之后,我終于有了一個工作系統。 瀏覽Play 2.0上的文檔時,我注意到他們從2.0開始提供了自己的Json庫。 由于我在先前版本中使用了Json-Lift,因此我認為將代碼移到Play提供的名為Jerkson的Json庫中是一個不錯的練習。
搬到杰克森
遷移到新圖書館很容易。 Lift-Json和Jerkson都使用幾乎相同的概念來構建Json對象。 在舊版本中,我沒有使用任何自動編組(因為我必須遵守jsongeo格式),因此在此版本中,我也手動進行了編組。 在下一個清單中,您可以同時看到舊版本和新版本。正如您所看到的,兩者中使用的概念幾乎相同。
#New version using jerksonval jsonstring = JsObject(List("type" -> JsString("featureCollection"),"features" -> JsArray(records.map(r =>(JsObject(List("type" -> JsString("Feature"),"gm_naam" -> JsString(r.name),"geometry" -> Json.parse(r.geojson),"properties" -> ({ var toAdd = List[(String, play.api.libs.json.JsValue)]()r.properties.foreach(entry => (toAdd ::= entry._1 -> JsString(entry._2)))JsObject(toAdd)}))))).toList)))#Old version using Lift-Jsonval json =("type" -> "featureCollection") ~("features" -> records.map(r =>(("type" -> "Feature") ~("gm_naam" -> r.name) ~("geometry" -> parse(r.geojson)) ~("properties" -> ({// create an empty objectvar obj = JNothing(0)// iterate over the propertiesr.properties.foreach(entry => (// add each property to the object, the reason// we do this is, that else it results in an // arraylist, not a list of seperate propertiesobj = concat(obj, JField(entry._1, entry._2))))obj})))))
畢竟,我擁有的已經完全一樣。 但是現在使用Play 2.0,并且不使用任何外部庫(Querulous除外)。 到目前為止,我在Play 2.0方面的經歷非常積極。 缺少好的具體示例和文檔有時會令人討厭,但可以理解。 它們確實在分發中提供了幾個廣泛的示例,但沒有與我的用例匹配的示例。 因此,對負責Play 2.0的人表示敬意。 到目前為止,我所看到的是,出色而全面的框架,許多功能以及一個可以進行scala編程的良好環境。在接下來的幾周中,我將看看是否有足夠的勇氣開始使用Anorm,并且我將看看Play在客戶端可以提供什么。 到目前為止,我已經看過我真正喜歡的LESS,因此我對他們的模板解決方案充滿了希望;-)
參考: 播放2.0:Akka,Rest,Json和我們JCG合作伙伴的 依賴項 ? Smart Java博客中的Jos Dirksen。
翻譯自: https://www.javacodegeeks.com/2012/03/play-20-akka-rest-json-and-dependencies.html