Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting some REST with Spray

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Getting some REST with Spray

Adding a RESTful API layer to your application can be easy, fast and reliable, using Spray

Avatar for Nimrod Argov

Nimrod Argov

January 07, 2015
Tweet

Other Decks in Technology

Transcript

  1. Things We Don’t Want Containers XML Mutability Java under the

    surface <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>controlServlet</servlet-name> <servlet-class> com.jenkov.butterfly.ControlServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>controlServlet</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>
  2. Spray Under the Hood Scala Akka Actors & Futures Akka

    I/O (used to be spray-io) Thread Pool / Config / Logging by Akka
  3. Main Modules Server DSL (spray-routing) Client DSL (spray-client) Low-level HTTP

    Server/Client (spray-can) HTTP Data Model (spray-http) Test Kit (spray-testkit)
  4. Hello, World! val myRoute =
 path("hello"){
 complete {
 "world"
 }


    } object MyServiceActor extends App with SimpleRoutingApp with MyService {
 implicit val system = ActorSystem("MyService-System")
 
 startServer(interface = "localhost", port = 9090)(myRoute)
 }

  5. Route Composition Transform request / response for inner nested routes

    Filter requests for certain routes by Method Path Params … Route Chaining - Try a different route
  6. Directives - Chaining val route = a { b {

    c { ... // route 1 } ~ d { ... // route 2 } ~ ... // route 3 } ~ e { ... // route 4 } }
  7. Directives - Filtering val myRoute =
 path("hello") {
 get {


    complete {
 "world"
 }
 }
 } ~
 path("hi") {
 post {
 complete("Hello World!")
 }
 }
  8. Directives - Extraction val myRoute =
 path("student" / IntNumber){ id

    =>
 complete(s"student id received: $id")
 }
  9. Directives - Composition val getOrPost = get | post val

    myRoute = (path("test") & getOrPost) {
 complete("This could have been a get or a post only")
 }
  10. Directives - Composition val teacher = "teacher" / IntNumber
 


    val course = "course" / IntNumber
 
 val teacherCourse = path(teacher / course)
 val myRoute = teacherCourse { (teacherId, courseId) =>
 complete(s"Got teacher $teacherId and course $courseId")
 }
  11. Directives - Composition val teacher = "teacher" / IntNumber
 


    val course = "course" / IntNumber
 
 val teacherCourse = path(teacher / course)
 val myRoute = (teacherCourse & getOrPost){ (teacherId, courseId) =>
 complete(s"Got teacher $teacherId and course $courseId")
 }
  12. Directives - Composition val teacher = "teacher" / IntNumber
 


    val course = "course" / IntNumber
 
 val teacherCourse = path(teacher / course)
 val getOrPostTeacherCourse = teacherCourse & getOrPost val myRoute = getOrPostTeacherCourse { (teacherId, courseId) =>
 complete(s"Got teacher $teacherId and course $courseId")
 }
  13. Directives - Type Safety val teacher = "teacher" / IntNumber

    (teacher | get){ teacherId =>
 complete(s"Got teacher $teacherId")
 }
 DOES NOT COMPILE!
  14. Directives - Many Variations path("hi") {
 post {
 parameter("param".as[Int]) {

    param =>
 validate(param > 0, "Sorry, param has to be greater than zero"){
 complete("Hello World!" * param)
 }
 }
 }
 }
  15. Rejections val noSuchPageHandler = RejectionHandler {
 case Nil => complete(NotFound,

    "Sorry, it's not here!")
 }
 val myRoute = getOrPostTeacherCourse { (teacherId, courseId) =>
 handleRejections(noSuchPageHandler) {
 complete(s"Got teacher $teacherId and course $courseId")
 }
 }
  16. Rejections - Selective implicit val generalRejectionHandler = RejectionHandler {
 case

    MissingQueryParamRejection("id") :: _ => 
 complete(BadRequest, "Looks like a bug to me!")
 }
  17. Asynchronous Processing class CalculatingActor extends Actor {
 override def receive

    = {
 case _ => sender ! 42
 }
 } val calculator: ActorRef = 
 actorRefFactory.actorOf(Props[CalculatingActor]) path("calc") {
 complete{
 (calculator ? "calc").
 mapTo[Int].
 map(result => s"calculation result is $result")
 }
 }
  18. TestKit Support for specs2 and Scalatest No need for the

    actual server Does use an actor system
  19. TestKit class RouteTest extends Specification with Specs2RouteTest with MyService{ 


    def actorRefFactory = system
 
 "My REST service" should {
 "Answer a hello message" in {
 Get("/hello") ~> myRoute ~> check {
 responseAs[String] === "world"
 }
 } } }
  20. TestKit class RouteTest extends Specification with Specs2RouteTest with MyService{ 


    def actorRefFactory = system
 
 "My REST service" should {
 "Reject a POST request" in {
 Post("/hello") ~> myRoute ~> check {
 rejection === MethodRejection(HttpMethods.GET)
 }
 }
 }
 }

  21. TestKit class RouteTest extends Specification with Specs2RouteTest with MyService{ 


    def actorRefFactory = system
 
 "My REST service" should {
 "Reject a POST request" in {
 Post("/hello") ~> sealRoute(myRoute) ~> check {
 status === MethodNotAllowed
 }
 }
 }
 }