• Save
spray: REST on Akka
Upcoming SlideShare
Loading in...5
×
 

spray: REST on Akka

on

  • 11,154 views

The slides of my talk at @nescalas (the northeast scala symposium, nescala.org) on March 9th, 2012

The slides of my talk at @nescalas (the northeast scala symposium, nescala.org) on March 9th, 2012

Statistics

Views

Total Views
11,154
Views on SlideShare
8,848
Embed Views
2,306

Actions

Likes
15
Downloads
37
Comments
0

10 Embeds 2,306

http://marakana.com 1564
https://thenewcircle.com 548
http://lanyrd.com 147
https://twitter.com 30
http://tweetedtimes.com 6
http://mrkn.co 5
http://twitter.com 2
http://us-w1.rockmelt.com 2
http://feeds.feedburner.com 1
https://si0.twimg.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \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

spray: REST on Akka spray: REST on Akka Presentation Transcript

  • REST on Akka⇗ northeast scala symposium March 9th, 2012
  • What is spray?
  • What is spray?Suite of libraries for building and consumingRESTful web services on top of Akka
  • What is spray?Suite of libraries for building and consumingRESTful web services on top of Akka• First released about 1 year ago
  • What is spray?Suite of libraries for building and consumingRESTful web services on top of Akka• First released about 1 year ago• Principles: lightweight, async, non-blocking, actor-based, modular, few deps, testable
  • What is spray?Suite of libraries for building and consumingRESTful web services on top of Akka• First released about 1 year ago• Principles: lightweight, async, non-blocking, actor-based, modular, few deps, testable• Philosophy: library, not framework
  • Components
  • Components• Rich immutable HTTP model
  • Components• Rich immutable HTTP model• spray-server: DSL for server-side API construction
  • Components• Rich immutable HTTP model• spray-server: DSL for server-side API construction• spray-client: complementary HTTP client
  • Components• Rich immutable HTTP model• spray-server: DSL for server-side API construction• spray-client: complementary HTTP client• spray-can: low-level HTTP server and client
  • Components• Rich immutable HTTP model• spray-server: DSL for server-side API construction• spray-client: complementary HTTP client• spray-can: low-level HTTP server and client• spray-json: straight JSON in scala (no Akka)
  • spray-server
  • spray-server• Runs on servlet containers or spray-can
  • spray-server• Runs on servlet containers or spray-can• Tool for building a “self-contained” API layer
  • spray-server• Runs on servlet containers or spray-can• Tool for building a “self-contained” API layer• Central element: Routing DSL for defining web API behavior
  • spray-server• Runs on servlet containers 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
  • Basic Architecture Application Business Logic
  • Basic ArchitectureREST API layer Application Routing Logic Business Logic
  • Basic Architecture REST API layer ApplicationHTTP Request Routing Logic Business Logic
  • Basic Architecture REST API layer ApplicationHTTP Request Action Routing Logic Business Logic
  • Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic
  • Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic Reply
  • Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic Logic Reply
  • Basic Architecture REST API layer ApplicationHTTP Request Action domain Routing object ! Business Logic LogicHTTP Response Reply
  • API Layer Responsibilities
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding• Authentication / authorization
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding• Authentication / authorization• Caching and serving static content
  • API Layer Responsibilities• Request routing based on method, path, query parameters, entity• (Un)marshalling to / from domain objects• Encoding / decoding• Authentication / authorization• Caching and serving static content• RESTful error handling
  • 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 } } }
  • Routing BasicsRoutes in spray: type Route = RequestContext => Unit
  • Routing BasicsRoutes in spray: type Route = RequestContext => Unit Explicit continuation- passing style
  • Routing BasicsRoutes in spray: type Route = RequestContext => Unit Explicit continuation- passing styleCentral object: case class RequestContext( request: HttpRequest, ...) {   def complete(...) { ... }   def reject(...) { ... }   ... }
  • Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")
  • Routing BasicsThe simplest route: ctx => ctx.complete("Say hello to spray")or: _.complete("Say hello to spray")
  • 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")
  • 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)
  • 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 } } }
  • 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 } } }
  • 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 } } }
  • 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 } } }
  • 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 }
  • 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 } } }
  • 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 } } }
  • 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!
  • DirectivesDRYing up with the `|` operator:val route = path("order" / HexIntNumber) { id => (get | put) { ctx => ctx.complete("Received " + ctx.request.method + " request for order " + id) } }
  • 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) } }
  • 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) }
  • 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) }
  • DirectivesOperators are type-safe:val orderPath = path("order" / IntNumber)
  • Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)
  • Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | get
  • Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | get
  • Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)
  • Directives Compiles?Operators are type-safe:val orderPath = path("order" / IntNumber)val dir = orderPath | getval dir = orderPath | path("[^/]+".r / DoubleNumber)
  • 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])
  • 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])
  • 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 ?)
  • 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 ?)
  • 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}
  • 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
  • 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,
  • } } }~ } } 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") {
  • } }~ 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") } }}
  • Best Practices
  • Best Practices• Keep route structure clean and readable, pull out all logic into custom directives
  • Best Practices• Keep route structure clean and readable, pull out all logic into custom directives• Don’t let API layer leak into application
  • 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
  • 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• Wrap blocking code with `detach`
  • 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• Wrap blocking code with `detach`• Use sbt-revolver + JRebel for fast dev turn- around
  • There is more ...
  • There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport
  • There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming
  • There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes
  • There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes• RESTful errors
  • There is more ...• SprayJsonSupport, LiftJsonSupport, TwirlSupport, ScalateSupport• Asynchronous response push streaming• Testing spray routes• RESTful errors• spray-client
  • Current State
  • Current State• spray 0.9.0 just released (last release for Akka 1.x)
  • Current State• spray 0.9.0 just released (last release for Akka 1.x)• Current focus: Release first Akka 2.0 compatible milestone of spray 1.0
  • Current State• spray 0.9.0 just released (last release for Akka 1.x)• Current focus: Release first Akka 2.0 compatible milestone of spray 1.0• Coming features: new documentation site, deeper REST support, monitoring, request throttling, and more ...
  • A few current sprayers ...
  • Getting started• Main site & documentation: https://github.com/spray/spray/wiki• Mailing list: http://groups.google.com/group/spray-user• Twitter: @spraycc
  • Thank you!
  • More
  • 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") } } }