On Github thebignet / talk-akka-http
Présenté par Jean Detoeuf / @thebignet
Passionné de nouvelles technologies
#jvm #docker #craftmanship #rpi #diy
Je ne suis pas un expert en Scala
Soyez sympas ^^
version 2.0.1 lors de cette présentation
mélange entre Spray et Akka
J'ai eu l'idée de cette présentation à la version 1.0, il était temps !
Présenté au SLUG par Frédéric Masion en février 2015
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
Source ~> Flow1 ~> Flow2a ~> Sink ~> Flow2b
Fan-out / Fan-in
Constuit sur Akka Streams
Internet est un tuyau
rempli de chatons
Flow[HttpRequest, HttpResponse]
Client et serveur
Mettre des acteurs sur HTTP
Les exemples sont pour la plupart tirés de la documentation d'Akka
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 }
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 }
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 }
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 }
Comment passer d'un flux HTTP à Scala et inversement ?
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
Résolution implicite
Si le type ToEntityMarshaller[T] est défini, il est utilisé
Unmarshallers prédéfinis
Byte Short Int Long Float Double Boolean Array[Byte] ByteString Array[Char] String akka.http.scaladsl.model.FormData
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)
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)
Intercepte la requête
Filtrage de la requête
Transformation de la requête ou réponse
Extraire des informations
val route = path("order" / IntNumber) { id => (get | put) { extractMethod { m => complete(s"Received ${m.name} request for order $id") } } }
val orderGetOrPutWithMethod = path("order" / IntNumber) & (get | put) & extractMethod val route = orderGetOrPutWithMethod { (id, m) => complete(s"Received ${m.name} request for order $id") }
val matcher: PathMatcher1[Option[Int]] = "foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create")
Intercepte les chemins suivants :
foo/bar/X42/editet
foo/bar/X/create
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 } }
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 between 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 between 0 and 255") }
ValidationRejection si require ne passe pas
Par défaut : 400 Bad Request
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 } }
entityAs
handled
header
response
status
entre autres ...
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://akka.io"))