We believe your privacy is very important. We use cookies to track your behaviour and provide a better experience
Backend Helpers | Automation and Software Development for Cloud Applicationses
Spray is a collection of lightweight Scala libraries providing client and server-side REST/HTTP support on top of Akka toolkit. In this post we are going to create a simple restful API with spray.
Spray can run as an standalone service or inside a servlet container. There is a spray template project on github that can be used as starting point. This github repository has different branches, one branch for every possible configuration, we will clone this repo and we will use the branch on_spray-can_1.3_scala-2.11
. This branch contains the structure for a standalone spray-can, Scala 2.11 + Akka 2.3 + spray 1.3
git clone git@github.com:spray/spray-template.git git checkout on_spray-can_1.3_scala-2.11
Running tests:
sbt test
To run server you need to execute sbt
:
sbt re-start [info] Application demo not yet started [info] Starting application demo in the background ... demo Starting com.example.Boot.main() [success] Total time: 0 s, completed 15-Apr-2015 16:11:08 > demo [INFO] [04/15/2015 16:11:09.323] [on-spray-can-akka.actor.default-dispatcher-3] [akka://on-spray-can/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:8080
You can now request the applicaton using curl
command:
sbt > re-stop
Stopping application:
sbt re-stop
We are going to implement a basic CRUD for a user resource. Just Crate and Get for now.
In order to handle json serialization, we will usespray-json to create the class com.notempo1320.ApiJsonProtocol
that will implement a custom json protocol:
package com.notempo1320 import spray.httpx.SprayJsonSupport._ import spray.httpx.SprayJsonSupport import spray.json._ import DefaultJsonProtocol._ case class User(var id: Option[Long], username: String, email: String) object ApiJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport { //jsonFormatX depends of number of parameters that the object receives implicit val userFormat = jsonFormat3(User) }
We create now a class child of spray.routing.HttpService
toimplement the Restful http logic. This class uses functionality from the spray-routing module. This module provides a high-level routing DSL for defining RESTful web services.
// this trait defines our service behavior independently from the service actor trait UserService extends HttpService { var userList = new ListBuffer[User]() val userRoute = path("user") { get { respondWithMediaType('application/json') { userList.append(User(Option(util.Random.nextInt(10000).toLong), "user1", "email1")) userList.append(User(Option(util.Random.nextInt(10000).toLong), "user2", "email2")) complete(userList.toList.toJson.compactPrint) } } ~ post { entity(as[User]) { user => val user2 = User(Option(util.Random.nextInt(10000).toLong), user.username, user.email) userList += user2 respondWithMediaType('application/json') { complete(StatusCodes.Created, user2.toJson.compactPrint) } } } } }
The code above use routes to deliver the request to the appriate component.
Now we will create an Actor that will wrap UserService logic:
// we don't implement our route structure directly in the service actor because // we want to be able to test it independently, without having to spin up an actor class UserActor extends Actor with UserService { // the HttpService trait defines only one abstract member, which // connects the services environment to the enclosing actor or test def actorRefFactory = context // this actor only runs our route, but you could add // other things here, like request stream processing // or timeout handling def receive = runRoute(userRoute) }
Api class is a console command wich will be responsible to run spray-can server:
package com.notempo1320 import akka.actor.{ActorSystem, Props} import akka.io.IO import spray.can.Http import akka.pattern.ask import akka.util.Timeout import scala.concurrent.duration._ object Api extends App { // we need an ActorSystem to host our application in implicit val system = ActorSystem("on-spray-can") // create and start our service actor val service = system.actorOf(Props[UserActor], "user-service") implicit val timeout = Timeout(5.seconds) // start a new HTTP server on port 8080 with our service actor as the handler IO(Http) ? Http.Bind(service, interface = "localhost", port = 7000) }
organization := "com.notempo1320" version := "0.1" scalaVersion := "2.11.6" scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8") libraryDependencies ++= { val akkaV = "2.3.9" val sprayV = "1.3.3" Seq( "io.spray" %% "spray-can" % sprayV, "io.spray" %% "spray-routing" % sprayV, "io.spray" %% "spray-json" % "1.3.1", "io.spray" %% "spray-httpx" % sprayV, "io.spray" %% "spray-testkit" % sprayV % "test", "com.typesafe.akka" %% "akka-actor" % akkaV, "com.typesafe.akka" %% "akka-testkit" % akkaV % "test", "org.specs2" %% "specs2-core" % "2.3.11" % "test" ) } Revolver.settings mainClass in (Compile, run) := Some("com.notempo1320.Api") mainClass in Revolver.reStart := Some("com.notempo1320.Api")
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d ''{"username": "myname", "email": "mymail@server.com"}'' http://localhost:7000/user
curl http://localhost:7000/user
You can find source code of this example here