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]