Catecbol

Cliente Akka HTTP: HEAD val resp=Http().singleRequest(HttpRequest(HEAD,uriPosts)) resp.map{msg=> println("Status "+msg.status) msg.headers.foreach{cabezera=> println(cabezera.name+":"+cabezera.value). }.
2MB Größe 14 Downloads 100 vistas
Catecbol

Capacitación Tecnológica Científica para Bolivia

www.catecbol.com

facebook.com/catecbol

@catecbol

Servicios REST en Akka Jean-Paul Calbimonte University of Applied Sciences and Arts Western Switzerland, HES-SO

La unión es la fuerza

[email protected]

Jean-Paul Calbimonte Formación académica: • Ingeniería de Sistemas: Universidad Católica Boliviana, Bolivia • Master en Informática: École Polytechnique Fédérale de Lausanne EPFL, Suiza

• Doctorado en Inteligencia Artificial: Universidad Politécnica de Madrid, España Experiencia:

• Investigador adjunto, University of Applied Sciences and Arts Western Switzerland • Postdoctorado, École Polytechnique Fédérale de Lausanne EPFL, Suiza • Software dev: pirAMide informatik, medspazio, Cochabamba Área de Investigación: • Procesamiento de streams, RDF streams, ingeniería ontológica, e-Health

http://jeanpi.org

slido.com Preguntas en slido.com

usar el código #CATECBOL

REST

¿Qué es REST? REpresentational State Transfer • • • •

Arquitectura/diseño de API Web Basado en HTTP Stateless (sin estado) Todo objeto es un recurso Web

REST: ideas básicas Recurso: toda entidad que se quiera referenciar • e.g. entidad u objeto físico/abstracto • collección de entidades • URI: identificador (nombre) de un recurso • recursos: accessibles a travé de su URI

REST: URIs Ejemplos:

http://bolivia.bo/escritores

URI

una collección de escritores

http://bolivia.bo/escritores/nataniel_aguirre un escritor en particular

http://bolivia.bo/beni/cercado/trinidad pueden codificarse jerarquías

REST: Protocolo HTTP • Protocolo de comunicación • Petición-respuesta (request-response) • Interacciones cliente-servidor cabeceras método de petición (verbo)

cliente

código de respuesta

cuerpo cabeceras

cuerpo

petición servidor

respuesta

REST: Verbos HTTP

Verbos HTTP

Representan una invocación a una acción Interfaz uniforme / API para recursos GET PUT DELETE POST HEAD OPTIONS

Akka HTTP: un vistazo

Akka HTTP

Núcleo Akka HTTP

• librería para aplicaciones HTTP cliente/servidor • basada en Actores Akka y Streams • proveer y consumir servicios basados en HTTP Modelo HTTP

API Servidor

Modelo de URIs

API Cliente

Marshall/Unmarshall

Soporte WebSocket

Encoding/Decoding

Soporte JSON/XML Soporte HTTPS

Akka HTTP: cómo usar

Akka HTTP: cómo usar importar librería en sbt

importar paquetes

libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-http" % "10.0.10"

import akka.http.scaladsl.model._ import akka.http.scaladsl.model.HttpMethods._ import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.model.MediaTypes._

DSL para scala cabeceras, verbos, tipos, etc.

implicit val system=ActorSystem() implicit val materializer=ActorMaterializer()

• integrado con sistema de Actores de Akka • integrado con Streams de Akka

URIs http://bolivia.bo:3024/artistas/actores?nombre=jenny#parte scheme

authority

path

query

val uri1 = Uri.from(scheme="ftp", host="mi-servidor.com", port=8090, path="archivos/documento.zip") val uri2 = Uri.from(scheme="mailto",path="[email protected]") val uri3: Uri="http://mi-servidor.com:8080/directorio"

fragment

Peticiones HttpRequest // ejemplo de URI val uriPando=Uri("http://bolivia.bo/pando") // ejemplo de verbo val verbo=HttpMethods.POST // ejemplo de protocol val proto=HttpProtocols.`HTTP/1.1` // ejemplo de cabecera val cabecera=Accept(`text/plain`)

//ejemplo de cuerpo val datos="""{ "nombre":"Pando","capital":"Cobija"}""" val mensaje=HttpEntity(datos)

Peticiones HttpRequest // petición val peticion=HttpRequest( uri=uriPando, method=verbo, protocol=proto, headers=List(cabecera), entity=mensaje)

val uriPando=Uri("http://bolivia.bo/pando") val verbo=HttpMethods.POST

val proto=HttpProtocols.`HTTP/1.1` val cabecera=Accept(`text/plain`) val datos="""{ "nombre":"Pando","capital":"Cobija"}""" val mensaje=HttpEntity(datos)

// mas simple val peticion2=HttpRequest(POST,uriPando,List(cabecera),mensaje)

Respuestas HttpResponse //ejemplo de cuerpo val datos="""{ "nombre":"Pando","capital":"Cobija"}""" val respuesta=HttpResponse( StatusCodes.OK, List(ETag("737060cd8c284d8af7ad3082f209582d")), HttpEntity(`application/json`, datos) ) // respuesta 200 OK val respuesta1=HttpResponse(200) // respuesta NotFound val respuesta2=HttpResponse(NotFound, entity="No se pudo encontrar el recurso.")

Cliente Akka HTTP object Cliente { implicit val system=ActorSystem() implicit val materializer=ActorMaterializer()

def ejemploGETSimple ={ val paisURI="https://restcountries.eu/rest/v2/name/bolivia" val pais=Http().singleRequest(HttpRequest(GET,uri = paisURI)) pais.map{resp=> println(resp.entity) } }

Cliente Akka HTTP HttpEntity.Strict(application/json, [ {"name":"Bolivia (Plurinational State of)", "topLevelDomain":[".bo"], "alpha2Code":"BO", "alpha3Code":"BOL", "callingCodes":["591"], "capital":"Sucre", "altSpellings":["BO","Buliwya","Wuliwya","Plurinational State of Bolivia","Estado Plurinacional de Bolivia","Buliwya Mamallaqta","Wuliwya Suyu","Tetã Volívia"], "region":"Americas", "subregion":"South America", "population":10985059, "latlng":[-17.0,-65.0] …......

Cliente Akka HTTP: OPTIONS val uriPosts="https://jsonplaceholder.typicode.com/posts"

val resp=Http().singleRequest(HttpRequest(OPTIONS,uriPosts)) resp.map(msg=> println(msg.getHeader("Access-Control-Allow-Methods")))

Access-Control-Allow-Methods: GET, HEAD, PUT, PATCH, POST, DELETE

Cliente Akka HTTP: HEAD val resp=Http().singleRequest(HttpRequest(HEAD,uriPosts)) resp.map{msg=> println("Status "+msg.status) msg.headers.foreach{cabezera=> println(cabezera.name+":"+cabezera.value) } }

Cliente Akka HTTP: HEAD

Status 200 OK Date:Sat, 25 Nov 2017 08:18:06 GMT Connection:keep-alive Set-Cookie:__cfduid=d6682ee0ea16d1a67886efafba6f1511597886; Expires=Sun, 2 Nov 2018 08:18:06 GMT; Domain=typicode.com; Path=/; HttpOnly X-Powered-By:Express Vary:Origin, Accept-Encoding Access-Control-Allow-Credentials:true Cache-Control:public, max-age=14400 Pragma:no-cache Expires:Sat, 25 Nov 2017 12:18:06 GMT

Cliente Akka HTTP: GET val resp=Http().singleRequest(HttpRequest(GET,s"$uriPosts/1")) resp.map{msg=> println("Status "+msg.status) println("Content-Type "+msg.entity.contentType) println("Content-Length "+msg.entity.contentLengthOption.get) msg.entity.toStrict(5 seconds).map{ent=> println(ent.data.utf8String) } }

Cliente Akka HTTP: GET

Status 200 OK Content-Type application/json Content-Length 292 { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nrepre totam\nnostrum rerum est autem sunt rem eveniet architecto" }

Cliente Akka HTTP: DELETE

val resp=Http().singleRequest(HttpRequest(DELETE,s"$uriPosts/30")) resp.map{msg=> println("Status "+msg.status)} Status 404 Not Found

val resp2=Http().singleRequest(HttpRequest(DELETE,s"$uriPosts/3")) resp2.map{msg=> println("Status "+msg.status)} Status 200 OK

Cliente Akka HTTP: POST val mensaje=""" { "userId": 1, "title": "Un post corto", "body": "El contenido va aqui" }""" val cuerpo=HttpEntity(`application/json`,mensaje)

val resp=Http().singleRequest( HttpRequest(POST,s"$uriPosts",entity=cuerpo)) resp.map{msg=> println("Status "+msg.status) imprimirResponse(msg) }

Cliente Akka HTTP: PUT val mensaje=""" { "userId": 1, "title": "Un post corto", "body": "El contenido va aqui" }""" val cuerpo=HttpEntity(`application/json`,mensaje) val resp=Http() .singleRequest(HttpRequest(PUT,s"$uriPosts/3",entity=cuerpo)) resp.map{msg=> println("Status "+msg.status) imprimirResponse(msg) }

Cliente Akka HTTP: PATCH val mensaje=""" { "title": "Cambio de titulo" }""" val cuerpo=HttpEntity(`application/json`,mensaje) val resp=Http().singleRequest( HttpRequest(PATCH,s"$uriPosts/3",entity=cuerpo)) resp.map{msg=> println("Status "+msg.status) imprimirResponse(msg) }

Cliente Akka HTTP: Futuros

val future=Http().singleRequest(HttpRequest(GET,s"$uriPosts/1")) future.map(_.entity.dataBytes .map(a=>a.utf8String) .runWith(Sink.foreach(println)) )

Servidor Akka HTTP implicit val system = ActorSystem("my-system") implicit val materializer = ActorMaterializer() implicit val executionContext = system.dispatcher

def main(args: Array[String]) { // Iniciar el servidor (binding en el puerto especificado) val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

println(s"Servidor accesible en: http://localhost:8080/\n...") StdIn.readLine() // corre hasta que se presione enter bindingFuture .flatMap(_.unbind()) // lanzar el proceso de unbind .onComplete(_ => system.terminate()) // terminar el system de actores }

Rutas en Akka HTTP

val route = path("demo") { get { complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "

Servicio Disponible!

")) } }

Rutas en Akka HTTP val escritores=Seq( "nataniel-aguirre", "franz-tamayo", "augusto-cespedes", "oscar-cerruto") val rutaGet = get { pathPrefix("bolivia") { path("escritores") { complete(escritores.mkString(",")) } } }

Rutas en Akka HTTP val rutaGet = get { pathPrefix("bolivia") { path("escritores") { complete(escritores.mkString(",")) }~ path("pintores") { complete(pintores.mkString(",")) } } }

Directivas en Akka HTTP val rutaHeader = (path("ejemplo1") & headerValueByName(`User-Agent`.name)) {agent=> complete(s"El header User-Agent es: $agent") }~ (path("ejemplo2") & extractRequest) {req=> complete(s"Los headers: ${req.headers}") }~ (path("ejemplo3") & parameter('nombre)) { nombre=> complete(s"El parametro nombre: $nombre") }~ (path("ejemplo4") & parameter('edad) & parameter('talla)) { (nombre,talla)=> complete(s"Los parametros : $nombre $talla") }

Directivas en Akka HTTP

get { pathPrefix("bolivia") { path("escritores") { complete(escritores.mkString(",")) } } }

Directivas en Akka HTTP pathPrefix("bolivia") { path("escritores") { complete(escritores.mkString(",")) }~ path("escritores" / IntNumber) {i=> if (i val option=escritores.find(_.equals(nombre)) if (option.isDefined) complete(option.get) else complete(NotFound,s"No existe el escritor $nombre") }

Directivas en Akka HTTP val rutaEscritores2 = pathPrefix("bolivia") { pathPrefix("escritores") { (get & pathEnd){ complete(escritores.mkString(",")) }~ (post & extractRequest) {req=> crearEscritor(req) complete(OK,"para update") }~ (path(IntNumber) & delete) {i=> if (i>=escritores.size) complete(NotFound,s"No existe el escritor con id $i") else complete(OK,"para delete") } }}

Otras caracteristicas de Akka HTTP • • • • • • •

Manejo de excepciones TestKit para rutas Soporte WebSocket Soporte HTTPS API disponible en Java Soporte HTTP/2 Server Sent Events Soporte XML/JSON

Scala: IDEs: Eclipse, IntelliJ

http://scala-ide.org/

Scala: akka

http://akka.io/

Scala: play framework

https://playframework.com/

Scala: Apache Spark

https://spark.apache.org

Scala: Docs

http://docs.scala-lang.org/

Material disponible https://github.com/jpcik/tutorials

Catecbol www.catecbol.com

Capacitación Tecnológica Científica para Bolivia facebook.com/catecbol

@catecbol

Gracias [email protected] Jean-Paul Calbimonte

La unión es la fuerza

[email protected]