播放2.0:Akka,Rest,Json和依赖项

在过去的几个月中,我越来越多地涉足scala。 Scala与“ Play框架”一起为您提供了一个非常有效且快速的开发环境(即,您掌握了Scala语言的特质之后)。

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.org
 
The new application will be created in /Users/jos/Dev/play-2.0-RC2/FirstStepsWithPlay20
 
What is the application name? 
> FirstStepsWithPlay20
 
Which template do you want to use for this new application? 
 
  1 - Create a simple Scala application
  2 - Create a simple Java application
  3 - Create an empty project
 
> 1
 
OK, 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 controllers
 
import akkawebtemplate.GeoJsonService
import play.api.mvc.Action
import play.api.mvc.Controller
 
object 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 result
    case some: Command => {
 
      // execute the query from the FULL command and process the results using the
      // processRows function
      var records:Seq[GeoRecord] = null;
 
      // if the match parameter is null we do the normal query, if not we pass in a set of varargs
      some.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 string
      sender ! 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 actor
    val 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 String
    result
  }
}

很多代码,但是我想向您展示actor的定义以及如何使用它们。 总结一下,使用Akka执行请求/回复模式所需的Akka 2.0代码是这样的:

private def runCommand(command: Command):String =  {
 
    // get the actor
    val 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 String
    result
  }

这使用全局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 jerkson
    val 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-Json
    val json =
      ("type" -> "featureCollection") ~
        ("features" -> records.map(r =>
          (("type" -> "Feature") ~
            ("gm_naam" -> r.name) ~
            ("geometry" -> parse(r.geojson)) ~
            ("properties" -> ({
              // create an empty object
              var obj = JNothing(0)
              // iterate over the properties
              r.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 properties
                obj = 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值