//Karthik Srinivasan

Product Engineer, CTO & a Beer Enthusiast
Experiments, thoughts and scripts documented for posterity.

Quirky Personal Projects

LinkedIn

Email me

Spray.io REST service - Controller, Actions, Multiple controllers - Lesson 2

View the lessons list at https://github.com/karthik20522/SprayLearning

Once you have the Spray.io project setup (https://github.com/karthik20522/SprayLearning) you can find the boot file and controller files in the "src/main/scala/com/example" folder path. There are two files that are of interest. One is the Boot.scala and other MyService.scala. Boot.scala is basically the placeholder to define the controller (single, multi controllers) and the http binding. Actions of these controllers are coded in the MyService.scala file.

In the following example, I am renaming the MyService.scala to a more meaningful service to CustomerService.scala. Before we start working on the project would like to generate the eclipse project files for the code to be edited on Eclipse rather than Sublime or any text editor.

To get it setup on eclipse, you can run the "eclipse" command within the sbt console. Note that, in plugins.sbt you should have sbteclipse plugin. View the initial project setup https://github.com/karthik20522/SprayLearning

Following screenshot is the structure of the project within eclipse.



CustomerService.scala class:

This class basically is the controller class with Actions/routes defined in them. I personally like to separate the service behavior from service actor as we want to be able to test it independently without having to spin up an actor.
// 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 CustomerServiceActor extends Actor with CustomerService {

// 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 timeout handling
def receive = runRoute(customerRoutes)
}


// this trait defines our service behavior independently from the service actor
trait CustomerService extends HttpService {

val customerRoutes= ??? //routes are defined/implemented here

customerRoutes is the place holder where all the routes/actions are defined and implemented. An example of how routes look is as follows:
trait CustomerService extends HttpService {

  val customerRoutes =
    path("addCustomer") {
      post {
        complete {
          //insert customer information into a DB
          "Success"
        }
      }
    } ~
      path("getCustomer" / Segment) { customerId =>
        get {
          complete {
            //get customer from db using customerId as Key
            s"success ${customerId}"
          }
        }
      }
}
The urls for accessing the above routes are as :
    [GET] http://localhost:8080/getCustomer/123
    [POST] http://localhost:8080/addCustomer

How about multiple controllers? It is as easy as creating a new trait extending HttpService and piping into the existing route. For example, if we want to create an Ajax service for all search related operations, following is a code snippet:
class CustomerServiceActor extends Actor with CustomerService with AjaxService {

  def actorRefFactory = context
  def receive = runRoute(customerRoutes ~ ajaxRoutes)
}

trait AjaxService extends HttpService {
  val ajaxRoutes =
    path("search" / Segment) { query =>
      get {
        complete {
          //Free text search implementation
          s"success ${query}"
        }
      }
    }
}

// this trait defines our service behavior independently from the service actor
trait CustomerService extends HttpService {
  //. . . . . . //existing code
}

The above project can be downloaded at https://github.com/karthik20522/SprayLearning