AKKA HTTP
A.K.A. SPRAY 2.0
Présenté par Jean Detoeuf / @thebignet
JEAN DETOEUF
DÉVELOPPEUR
Passionné de nouvelles technologies
#jvm #docker #craftmanship #rpi #diy
AVERTISSEMENT
Je ne suis pas un expert en Scala
Soyez sympas ^^
PROJET EN COURS DE MATURATION
version lors de cette présentation2.0.1
mélange entre Spray et Akka
J'ai eu l'idée de cette présentation à la version 1.0, il était temps !
AKKA STREAMS
Présenté au SLUG par en février 2015Frédéric Masion
Source ~> Flow ~> Sink
Source décrit une source de données
Flow représente une transformation de ces données
Sink une opération terminale
AKKA STREAMS
Source ~> Flow1 ~> Flow2a ~> Sink
~> Flow2b
Fan-out / Fan-in
AKKA HTTP
Constuit sur Akka Streams
Internet est un tuyau
rempli de chatons
AKKA HTTP
Flow[HttpRequest, HttpResponse]
AKKA HTTP
Client et serveur
Mettre des acteurs sur HTTP
MODULES
akka-http-spray-json
akka-http-xml
akka-http-testkit
akka-http
akka-http-core
C'EST BON, MONTRE-MOI LE CODE
Les exemples sont pour la plupart tirés de la documentation
d'Akka
EXEMPLE SIMPLE
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
object Main extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val route =
path("hello") {
get {
complete {
<h1>Say hello to akka-http</h1>
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/nPress RETURN to stop...")
Console.readLine() // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
}
EXEMPLE SIMPLE
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
object Main extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val route =
path("hello") {
get {
complete {
<h1>Say hello to akka-http</h1>
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/nPress RETURN to stop...")
Console.readLine() // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
}
EXEMPLE SIMPLE
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
object Main extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val route =
path("hello") {
get {
complete {
<h1>Say hello to akka-http</h1>
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/nPress RETURN to stop...")
Console.readLine() // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
}
EXEMPLE SIMPLE
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
object Main extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val route =
path("hello") {
get {
complete {
<h1>Say hello to akka-http</h1>
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/nPress RETURN to stop...")
Console.readLine() // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
}
MARSHALLING/UNMARSHALLING
MARSHALLING/UNMARSHALLING
Comment passer d'un flux HTTP à Scala et inversement ?
MARSHALLING
Marshallers prédéfinis
Array[Byte]
ByteString
Array[Char]
String
akka.http.scaladsl.model.FormData
akka.http.scaladsl.model.MessageEntity
T <: akka.http.scaladsl.model.Multipart
T si ToEntityMarshaller[T] est présent
MARSHALLING
Résolution implicite
Si le type ToEntityMarshaller[T] est défini, il est utilisé
UNMARSHALLING
Unmarshallers prédéfinis
Byte
Short
Int
Long
Float
Double
Boolean
Array[Byte]
ByteString
Array[Char]
String
akka.http.scaladsl.model.FormData
ROUTAGE
LOW-LEVEL
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
HttpResponse(entity = HttpEntity(ContentTypes.`text/html(UTF-8)`
,
"Hello world!"))
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sys.error("BOOM!")
case _: HttpRequest =>
HttpResponse(404, entity = "Unknown resource!")
}
Http().bindAndHandleSync(requestHandler, "localhost", 8080)
HIGH-LEVEL
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
val route =
get {
pathSingleSlash {
complete {
<html>
<body>Hello world!</body>
</html>
}
} ~
path("ping") {
complete("PONG!")
} ~
path("crash") {
sys.error("BOOM!")
}
}
// `route` will be implicitly converted to `Flow` using `RouteResult
.route2HandlerFlow`
Http().bindAndHandle(route, "localhost", 8080)
DIRECTIVES
DIRECTIVES
Intercepte la requête
Filtrage de la requête
Transformation de la requête ou réponse
Extraire des informations
EXEMPLE
val route =
path("order" / IntNumber) { id =>
(get | put) {
extractMethod { m =>
complete(s"Received ${m.name} request for order $id")
}
}
}
EXEMPLE 2
val orderGetOrPutWithMethod =
path("order" / IntNumber) & (get | put) & extractMethod
val route = orderGetOrPutWithMethod { (id, m) =>
complete(s"Received ${m.name} request for order $id")
}
et
PATHMATCHER
val matcher: PathMatcher1[Option[Int]] =
"foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create")
Intercepte les chemins suivants :
foo/bar/X42/edit
foo/bar/X/create
EXTRACTION CASE CLASS
case class Color(red: Int, green: Int, blue: Int)
val route = path("color") {
parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]).as(Color)
{
color =>
// utiliser color
}
}
VALIDATION CASE CLASS
case class Color(name: String, red: Int, green: Int, blue: Int) {
require(!name.isEmpty, "color name must not be empty")
require(0 <= red && red <= 255, "red color component must be betwe
en 0 and 255")
require(0 <= green && green <= 255, "green color component must be
between 0 and 255")
require(0 <= blue && blue <= 255, "blue color component must be be
tween 0 and 255")
}
ValidationRejection si require ne passe pas
Par défaut : 400 Bad Request
TESTS
Pour la route suivante
val smallRoute =
get {
pathSingleSlash {
complete {
"Hello World !"
}
} ~
path("ping") {
complete("PONG !")
}
}
Test
"Hello World ! non renvoyé sur /" in {
Get() ~> smallRoute ~> check {
status === StatusCodes.OK
responseAs[String] shouldEqual "Hello World !"
}
}
Pour la même route ...
val smallRoute =
get {
pathSingleSlash {
complete {
"Hello World !"
}
} ~
path("ping") {
complete("PONG !")
}
}
Test
"GET sur chemin inconnu non pris en compte" in {
Get("/pouet") ~> smallRoute ~> check {
handled shouldBe false
}
}
VARIABLES UTILISABLES DANS LES
TESTS
entityAs
handled
header
response
status
entre autres ...
CLIENT
val responseFuture: Future[HttpResponse] =
Http().singleRequest(HttpRequest(uri = "http://akka.io"))
QUESTIONS ?
MERCI POUR VOTRE ÉCOUTE
Cette présentation :
@thebignet
thebignet
thebignet
talk-akka-http

Akka http 2

  • 1.
    AKKA HTTP A.K.A. SPRAY2.0 Présenté par Jean Detoeuf / @thebignet
  • 2.
    JEAN DETOEUF DÉVELOPPEUR Passionné denouvelles technologies #jvm #docker #craftmanship #rpi #diy
  • 3.
    AVERTISSEMENT Je ne suispas un expert en Scala Soyez sympas ^^
  • 4.
    PROJET EN COURSDE MATURATION version lors de cette présentation2.0.1 mélange entre Spray et Akka J'ai eu l'idée de cette présentation à la version 1.0, il était temps !
  • 5.
    AKKA STREAMS Présenté auSLUG par en février 2015Frédéric Masion Source ~> Flow ~> Sink Source décrit une source de données Flow représente une transformation de ces données Sink une opération terminale
  • 6.
    AKKA STREAMS Source ~>Flow1 ~> Flow2a ~> Sink ~> Flow2b Fan-out / Fan-in
  • 7.
    AKKA HTTP Constuit surAkka Streams Internet est un tuyau rempli de chatons
  • 8.
  • 9.
    AKKA HTTP Client etserveur Mettre des acteurs sur HTTP
  • 10.
  • 11.
    C'EST BON, MONTRE-MOILE CODE Les exemples sont pour la plupart tirés de la documentation d'Akka
  • 12.
    EXEMPLE SIMPLE import akka.http.scaladsl.Http importakka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ import akka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer object Main extends App { implicit val system = ActorSystem("my-system") implicit val materializer = ActorMaterializer() implicit val ec = system.dispatcher val route = path("hello") { get { complete { <h1>Say hello to akka-http</h1> } } } val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) println(s"Server online at http://localhost:8080/nPress RETURN to stop...") Console.readLine() // for the future transformations bindingFuture .flatMap(_.unbind()) // trigger unbinding from the port .onComplete(_ ⇒ system.shutdown()) // and shutdown when done }
  • 13.
    EXEMPLE SIMPLE import akka.http.scaladsl.Http importakka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ import akka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer object Main extends App { implicit val system = ActorSystem("my-system") implicit val materializer = ActorMaterializer() implicit val ec = system.dispatcher val route = path("hello") { get { complete { <h1>Say hello to akka-http</h1> } } } val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) println(s"Server online at http://localhost:8080/nPress RETURN to stop...") Console.readLine() // for the future transformations bindingFuture .flatMap(_.unbind()) // trigger unbinding from the port .onComplete(_ ⇒ system.shutdown()) // and shutdown when done }
  • 14.
    EXEMPLE SIMPLE import akka.http.scaladsl.Http importakka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ import akka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer object Main extends App { implicit val system = ActorSystem("my-system") implicit val materializer = ActorMaterializer() implicit val ec = system.dispatcher val route = path("hello") { get { complete { <h1>Say hello to akka-http</h1> } } } val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) println(s"Server online at http://localhost:8080/nPress RETURN to stop...") Console.readLine() // for the future transformations bindingFuture .flatMap(_.unbind()) // trigger unbinding from the port .onComplete(_ ⇒ system.shutdown()) // and shutdown when done }
  • 15.
    EXEMPLE SIMPLE import akka.http.scaladsl.Http importakka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ import akka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer object Main extends App { implicit val system = ActorSystem("my-system") implicit val materializer = ActorMaterializer() implicit val ec = system.dispatcher val route = path("hello") { get { complete { <h1>Say hello to akka-http</h1> } } } val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) println(s"Server online at http://localhost:8080/nPress RETURN to stop...") Console.readLine() // for the future transformations bindingFuture .flatMap(_.unbind()) // trigger unbinding from the port .onComplete(_ ⇒ system.shutdown()) // and shutdown when done }
  • 16.
  • 17.
    MARSHALLING/UNMARSHALLING Comment passer d'unflux HTTP à Scala et inversement ?
  • 18.
  • 19.
    MARSHALLING Résolution implicite Si letype ToEntityMarshaller[T] est défini, il est utilisé
  • 20.
  • 21.
  • 22.
    LOW-LEVEL import akka.http.scaladsl.Http import akka.http.scaladsl.model.HttpMethods._ importakka.http.scaladsl.model._ import akka.stream.ActorMaterializer implicit val system = ActorSystem() implicit val materializer = ActorMaterializer() val requestHandler: HttpRequest => HttpResponse = { case HttpRequest(GET, Uri.Path("/"), _, _, _) => HttpResponse(entity = HttpEntity(ContentTypes.`text/html(UTF-8)` , "Hello world!")) case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => HttpResponse(entity = "PONG!") case HttpRequest(GET, Uri.Path("/crash"), _, _, _) => sys.error("BOOM!") case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!") } Http().bindAndHandleSync(requestHandler, "localhost", 8080)
  • 23.
    HIGH-LEVEL import akka.http.scaladsl.Http import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ importakka.http.scaladsl.server.Directives._ import akka.stream.ActorMaterializer implicit val system = ActorSystem() implicit val materializer = ActorMaterializer() val route = get { pathSingleSlash { complete { <html> <body>Hello world!</body> </html> } } ~ path("ping") { complete("PONG!") } ~ path("crash") { sys.error("BOOM!") } }
  • 24.
    // `route` willbe implicitly converted to `Flow` using `RouteResult .route2HandlerFlow` Http().bindAndHandle(route, "localhost", 8080)
  • 25.
  • 26.
    DIRECTIVES Intercepte la requête Filtragede la requête Transformation de la requête ou réponse Extraire des informations
  • 27.
    EXEMPLE val route = path("order"/ IntNumber) { id => (get | put) { extractMethod { m => complete(s"Received ${m.name} request for order $id") } } }
  • 28.
    EXEMPLE 2 val orderGetOrPutWithMethod= path("order" / IntNumber) & (get | put) & extractMethod val route = orderGetOrPutWithMethod { (id, m) => complete(s"Received ${m.name} request for order $id") }
  • 29.
    et PATHMATCHER val matcher: PathMatcher1[Option[Int]]= "foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create") Intercepte les chemins suivants : foo/bar/X42/edit foo/bar/X/create
  • 30.
    EXTRACTION CASE CLASS caseclass Color(red: Int, green: Int, blue: Int) val route = path("color") { parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]).as(Color) { color => // utiliser color } }
  • 31.
    VALIDATION CASE CLASS caseclass Color(name: String, red: Int, green: Int, blue: Int) { require(!name.isEmpty, "color name must not be empty") require(0 <= red && red <= 255, "red color component must be betwe en 0 and 255") require(0 <= green && green <= 255, "green color component must be between 0 and 255") require(0 <= blue && blue <= 255, "blue color component must be be tween 0 and 255") } ValidationRejection si require ne passe pas Par défaut : 400 Bad Request
  • 32.
  • 33.
    Pour la routesuivante val smallRoute = get { pathSingleSlash { complete { "Hello World !" } } ~ path("ping") { complete("PONG !") } } Test "Hello World ! non renvoyé sur /" in { Get() ~> smallRoute ~> check { status === StatusCodes.OK responseAs[String] shouldEqual "Hello World !" } }
  • 34.
    Pour la mêmeroute ... val smallRoute = get { pathSingleSlash { complete { "Hello World !" } } ~ path("ping") { complete("PONG !") } } Test "GET sur chemin inconnu non pris en compte" in { Get("/pouet") ~> smallRoute ~> check { handled shouldBe false } }
  • 35.
    VARIABLES UTILISABLES DANSLES TESTS entityAs handled header response status entre autres ...
  • 36.
    CLIENT val responseFuture: Future[HttpResponse]= Http().singleRequest(HttpRequest(uri = "http://akka.io"))
  • 37.
  • 38.
    MERCI POUR VOTREÉCOUTE Cette présentation : @thebignet thebignet thebignet talk-akka-http