Your SlideShare is downloading. ×
spray: REST on Akka (Scala Days)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

spray: REST on Akka (Scala Days)

13,823
views

Published on

Slides from the talk at the Scala Days 2012 in London

Slides from the talk at the Scala Days 2012 in London

Published in: Technology, Business

1 Comment
38 Likes
Statistics
Notes
No Downloads
Views
Total Views
13,823
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
1
Likes
38
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Transcript

    • 1. REST on Akka Scala DaysApril 17th, 2012
    • 2. What is spray?
    • 3. What is spray?Vision: Provide the best toolkit for REST/HTTPand low-level network-IO on top of Akka
    • 4. What is spray?Vision: Provide the best toolkit for REST/HTTPand low-level network-IO on top of Akka• First released about 1 year ago
    • 5. What is spray?Vision: Provide the best toolkit for REST/HTTPand low-level network-IO on top of Akka• First released about 1 year ago• Principles: lightweight, async, non-blocking, actor-based, modular, few deps, testable
    • 6. What is spray?Vision: Provide the best toolkit for REST/HTTPand low-level network-IO on top of Akka• First released about 1 year ago• Principles: lightweight, async, non-blocking, actor-based, modular, few deps, testable• Philosophy: set of libraries, not framework
    • 7. Current State
    • 8. Current StateTransitioning from Akka 1.3 to Akka 2.0
    • 9. Current StateTransitioning from Akka 1.3 to Akka 2.0• spray 0.9.0 for Akka 1.3 released in March
    • 10. Current StateTransitioning from Akka 1.3 to Akka 2.0• spray 0.9.0 for Akka 1.3 released in March• spray 1.0-M1 for Akka 2.0 two weeks ago
    • 11. Current StateTransitioning from Akka 1.3 to Akka 2.0• spray 0.9.0 for Akka 1.3 released in March• spray 1.0-M1 for Akka 2.0 two weeks ago• Next: second milestone of spray 1.0
    • 12. Current StateTransitioning from Akka 1.3 to Akka 2.0• spray 0.9.0 for Akka 1.3 released in March• spray 1.0-M1 for Akka 2.0 two weeks ago• Next: second milestone of spray 1.0 Focus of this talk
    • 13. Componentsspray-routing spray-clientspray-servlet spray-can spray-json spray-http spray-io
    • 14. Componentsspray-routing spray-clientspray-servlet spray-can spray-json spray-http spray-io Low-level, “actorized” network IO
    • 15. Components spray-routing spray-client spray-servlet spray-can spray-json spray-http spray-iorich immutable HTTP model (no Akka)
    • 16. Componentsspray-routing spray-clientspray-servlet spray-can spray-json low-level HTTP server and client spray-http spray-io
    • 17. Components spray-routing spray-client spray-servlet spray-can spray-json Servlet APIadapter layer spray-http spray-io
    • 18. Components spray-routing spray-clientDSL for server-side API construction spray-servlet spray-can spray-json spray-http spray-io
    • 19. Componentsspray-routing spray-client Complementary high- level HTTP clientspray-servlet spray-can spray-json spray-http spray-io
    • 20. Componentsspray-routing spray-clientspray-servlet spray-can spray-json straight JSON in Scala (no Akka) spray-http spray-io
    • 21. Components spray-routing spray-client Focus for therest of the talk spray-servlet spray-can spray-json spray-http spray-io
    • 22. spray-routing
    • 23. spray-routing• Runs on spray-servlet or spray-can
    • 24. spray-routing• Runs on spray-servlet or spray-can• Tool for building a “self-contained” API layer
    • 25. spray-routing• Runs on spray-servlet or spray-can• Tool for building a “self-contained” API layer• Central element: Routing DSL for defining web API behavior
    • 26. spray-routing• Runs on spray-servlet or spray-can• Tool for building a “self-contained” API layer• Central element: Routing DSL for defining web API behavior• Focus: RESTful web API, not web GUI
    • 27. Basic Architecture Application Business Logic
    • 28. Basic ArchitectureREST API layer Application Routing Logic Business Logic
    • 29. Basic Architecture REST API layer ApplicationHTTP Request Routing Logic Business Logic
    • 30. Basic Architecture REST API layer ApplicationHTTP Request Action Routing Logic Business Logic
    • 31. Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic
    • 32. Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic Reply
    • 33. Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic Reply
    • 34. Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic LogicHTTP Response Reply
    • 35. API Layer Responsibilities
    • 36. API Layer Responsibilities• Request routing based on method, path, query parameters, entity
    • 37. API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects
    • 38. API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding (compression)
    • 39. API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding (compression)• Authentication / authorization
    • 40. API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding (compression)• Authentication / authorization• Caching and serving static content
    • 41. API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding (compression)• Authentication / authorization• Caching and serving static content• RESTful error handling
    • 42. API Building in spray
    • 43. API Building in spray• HTTP messages are actor messages
    • 44. API Building in spray• HTTP messages are actor messages class PingServiceActor extends Actor { def receive = { case HttpRequest(GET, "/ping", _, _, _) => sender ! HttpResponse(200, "PONG") } }
    • 45. API Building in spray• HTTP messages are actor messages class PingServiceActor extends Actor { def receive = { case HttpRequest(GET, "/ping", _, _, _) => sender ! HttpResponse(200, "PONG") } }• Could build services only via pattern-matching
    • 46. API Building in spray• HTTP messages are actor messages class PingServiceActor extends Actor { def receive = { case HttpRequest(GET, "/ping", _, _, _) => sender ! HttpResponse(200, "PONG") } }• Could build services only via pattern-matching• But: pattern-matching becomes awkward for more complex service definitions
    • 47. API Building in spray: DSLSimple example:class MyServiceActor extends Actor with Routing { def receive = receiveFromRoute { path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } } ~ put { completeWith { "Received PUT request for order " + id } } } }}
    • 48. sprays Routing DSL
    • 49. sprays Routing DSL• Built from recombinant elements called “directives”
    • 50. sprays Routing DSL• Built from recombinant elements called “directives”• Concise, readable, maintainable
    • 51. sprays Routing DSL• Built from recombinant elements called “directives”• Concise, readable, maintainable• Highly composable
    • 52. sprays Routing DSL• Built from recombinant elements called “directives”• Concise, readable, maintainable• Highly composable• Type-safe
    • 53. sprays Routing DSL• Built from recombinant elements called “directives”• Concise, readable, maintainable• Highly composable• Type-safe• Easily extensible with custom constructs
    • 54. sprays Routing DSL• Built from recombinant elements called “directives”• Concise, readable, maintainable• Highly composable• Type-safe• Easily extensible with custom constructs• Directly interfaces with Akka API
    • 55. Routing BasicsRoutes in spray: type Route = RequestContext => Unit
    • 56. Routing BasicsRoutes in spray: type Route = RequestContext => Unit Explicit continuation- passing style
    • 57. Routing BasicsRoutes in spray: type Route = RequestContext => Unit Explicit continuation- passing styleCentral object: case class RequestContext( request: HttpRequest, ...) {   def complete(...) { ... }   def reject(...) { ... }   ... }
    • 58. Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")
    • 59. Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")or: _.complete("Say hello to spray")
    • 60. Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")or: _.complete("Say hello to spray")or using a “directive”: completeWith("Say hello to spray")
    • 61. Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")or: _.complete("Say hello to spray")or using a “directive”: completeWith("Say hello to spray") def completeWith[T :Marshaller](value: => T): Route = _.complete(value)
    • 62. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } }
    • 63. Directives Route structure built with directives: val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id }directive }~ name put { completeWith { "Received PUT request for order " + id } } }
    • 64. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ args put { completeWith { "Received PUT request for order " + id } } }
    • 65. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { extractions completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } }
    • 66. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } inner route }
    • 67. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id }~ } route concatenation: put { recover from rejections completeWith { "Received PUT request for order " + id } } }
    • 68. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } }
    • 69. DirectivesRoute structure built with directives:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } Route structure } forms a tree!
    • 70. Directivesspray 0.9.0 comes with 69 predefined directives:alwaysPass, authenticate, authorize, autoChunk, cache, cacheResults, clientIP,completeWith, content, cookie, decodeRequest, delete, deleteCookie, detach,dynamic, encodeResponse, filter, filter1, filter2, filter3, filter4, filter5, filter6,filter7, filter8, filter9, formField, formFields, get, getFromDirectory, getFromFile,getFromFileName, getFromResource, getFromResourceDirectory, handleWith,hardFail, head, headerValue, headerValuePF, host, jsonpWithParameter, method,optionalCookie, options, parameter, parameters, path, pathPrefix, post,produce, provide, put, redirect, reject, respondWithContentType,respondWithHeader, respondWithHeaders, respondWithMediaType,respondWithStatus, setCookie, trace, transformChunkedResponse,transformRejections, transformRequest, transformRequestContext,transformResponse, transformRoute, transformUnchunkedResponse, validate
    • 71. Real World Examplelazy val route = { encodeResponse(Gzip) { path("") { get { redirect("/doc") } }~ pathPrefix("api") { jsonpWithParameter("callback") { path("top-articles") { get { parameter(max.as[Int]) { max => validate(max >= 0, "query parameter max must be >= 0") { completeWith { (topArticlesService ? max).mapTo[Seq[Article]] } } } } }~ tokenAuthenticate { user => path("ranking") { get { countAndTime(user, "ranking") { parameters(fixed ? 0, mobile ? 0, sms ? 0, mms ? 0,
    • 72. } } }~ } } Real World Example tokenAuthenticate { user => path("ranking") { get { countAndTime(user, "ranking") { parameters(fixed ? 0, mobile ? 0, sms ? 0, mms ? 0, data ? 0).as(RankingDescriptor) { descr => (rankingService ? Ranking(descr)).mapTo[RankingResult] } } } }~ path("accounts") { post { authorize(user.isAdmin) { content(as[AccountDetails]) { details => (accountService ? NewAccount(details)).mapTo[OpResult] } } } }~ path("account" / IntNumber) { accountId => get { ... } ~ put { ... } ~ delete { ... } } }}~pathPrefix("v1") {
    • 73. } }~ Real World Example path("account" / IntNumber) { accountId => get { ... } ~ put { ... } ~ delete { ... } } } }~ pathPrefix("v1") { proxyToDjango } }~ pathPrefix("doc") { respondWithHeader(`Cache-Control`(`max-age`(3600))) { transformResponse(_.withContentTransformed(markdown2Html)) { getFromResourceDirectory("doc/root", pathRewriter = appendFileExt) } } }~ }~ cacheIfEnabled { encodeResponse(Gzip) { getFromResourceDirectory("public") } }}
    • 74. Best Practices
    • 75. Best Practices• Keep route structure clean and readable, pull out all logic into custom directives
    • 76. Best Practices• Keep route structure clean and readable, pull out all logic into custom directives• Don’t let API layer leak into application
    • 77. Best Practices• Keep route structure clean and readable, pull out all logic into custom directives• Don’t let API layer leak into application• Use (Un)marshalling infrastructure
    • 78. Best Practices• Keep route structure clean and readable, pull out all logic into custom directives• Don’t let API layer leak into application• Use (Un)marshalling infrastructure• Use sbt-revolver + JRebel for fast dev turn- around
    • 79. There is more ...
    • 80. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport
    • 81. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming
    • 82. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes
    • 83. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes• RESTful errors
    • 84. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes• RESTful errors• spray-client
    • 85. There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes• RESTful errors• spray-client• spray-io
    • 86. What’s next?
    • 87. What’s next?• release 1.0 featuring improved and simplified API for Akka 2.0
    • 88. What’s next?• release 1.0 featuring improved and simplified API for Akka 2.0• Better and deeper documentation (follow Akkas model of treating docs as code)
    • 89. What’s next?• release 1.0 featuring improved and simplified API for Akka 2.0• Better and deeper documentation (follow Akkas model of treating docs as code)• Coming features: deeper REST support, monitoring, request throttling, ...
    • 90. What’s next?• release 1.0 featuring improved and simplified API for Akka 2.0• Better and deeper documentation (follow Akkas model of treating docs as code)• Coming features: deeper REST support, monitoring, request throttling, ...• Possibly: websockets, SPDY
    • 91. A few current sprayers ...
    • 92. Getting started• Main site & documentation: https://github.com/spray/spray/wiki• Mailing list: http://groups.google.com/group/spray-user• Twitter: @spraycc
    • 93. Thank you!
    • 94. More
    • 95. Route ExampleA simple spray route:val route: Route = path("order" / HexIntNumber) { id => get { completeWith { "Received GET request for order " + id } }~ put { completeWith { "Received PUT request for order " + id } } }
    • 96. DirectivesDRYing up with the `|` operator:val route = path("order" / HexIntNumber) { id => (get | put) { ctx => ctx.complete("Received " + ctx.request.method + " request for order " + id) } }
    • 97. DirectivesPulling out a custom directive:val getOrPut = get | putval route = path("order" / HexIntNumber) { id => getOrPut { ctx => ctx.complete("Received " + ctx.request.method + " request for order " + id) } }
    • 98. DirectivesThe `&` operator as alternative to nesting:val getOrPut = get | putval route = (path("order" / HexIntNumber) & getOrPut) { id => ctx => ctx.complete("Received " + ctx.request.method + " request for order " + id) }
    • 99. DirectivesPulling out once more:val orderGetOrPut = path("order" / HexIntNumber) & (get | put)val route = orderGetOrPut { id => ctx => ctx.complete("Received " + ctx.request.method + " request for order " + id) }
    • 100. DirectivesOperators are type-safe:val orderPath = path("order" / IntNumber)
    • 101. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)
    • 102. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | get
    • 103. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | get
    • 104. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)
    • 105. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)
    • 106. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)val dir = orderPath | parameter(order.as[Int])
    • 107. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)val dir = orderPath | parameter(order.as[Int])
    • 108. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)val dir = orderPath | parameter(order.as[Int])val order = orderPath & parameters(oem, expired ?)
    • 109. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)val dir = orderPath | parameter(order.as[Int])val order = orderPath & parameters(oem, expired ?)
    • 110. Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)val dir = orderPath | parameter(order.as[Int])val order = orderPath & parameters(oem, expired ?)val route = order { (orderId, oem, expired) => ... // inner route}
    • 111. Proxying with sprayCombining with spray-client to build a proxy:val conduit = new HttpConduit("target.example.com", 8080)lazy val proxyToTarget: Route = { ctx => ctx.complete { conduit.sendReceive { ctx.request.withHeadersTransformed { _.filter(_.name != "Host") } }.map { _.withHeadersTransformed { _.filter(_.name != "Date") } } }

    ×