Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Servicios Web REST - Una introducción a REST

Servicios Web REST - Una introducción a REST

En esta charla veremos los principios y las bases de toda API REST

Avatar for Miguel Ángel Sánchez Chordi

Miguel Ángel Sánchez Chordi

September 14, 2015
Tweet

More Decks by Miguel Ángel Sánchez Chordi

Other Decks in Programming

Transcript

  1. Contenido de la charla • Qué es REST? o Definición

    o Conceptos o Verbos HTTP o Códigos de respuesta o Ejemplos • APIs CRUD o Operaciones sobre recursos o Problemas de concurrencia o Operaciones asíncronas • Seguridad en REST • Testing y pruebas
  2. Sobre mi • Programador web PHP desde hace 4 años

    (aprox. 1 con Symfony2). • Muy fan de NodeJS, Angular y Javascript en general • Aficionado a la seguridad informática y redes.
  3. Qué es REST? ➢ Es una tecnología? ✗ ➢ Es

    una arquitectura? ✗ ➢ Es un “estilo arquitectónico”?
  4. Qué es REST? ➢ Es una tecnología? ✗ ➢ Es

    una arquitectura? ✗ ➢ Es un “estilo arquitectónico”? ✓ Cuando hablamos de REST nos referimos a una forma de implementar una arquitectura en concreto (arquitectura WebService) por eso solemos decir que REST es sólo un estilo de elaborar servicios web.
  5. ¿Qué es REST? • La primera vez que se habló

    de REST fue en el año 2000 en la tesis doctoral de Roy Fielding, uno de los principales autores del protocolo HTTP • Está orientado a transferencias de recursos, no a llamadas de procedimientos remotos (RPC) • Hace un uso muy eficiente del protocolo HTTP, rompe con el esquema de la web que funciona solo con GET y POST
  6. REST en la actualidad • A día de hoy se

    conoce por REST a casi cualquier servicio Web que trabaje con XML y HTTP • La mayor parte de las APIs existentes son simples interfaces para interactuar con la base de datos (CRUD básico) • REST nos permite modelar totalmente nuestro negocio sin importar el lenguaje en que este implementado el servicio o el cliente
  7. Conceptos y reglas • El servicio representará recursos, no acciones

    o No se publican verbos como “comprar” o Se usan recursos como “pedido” o “compra” • Todos los recursos deben de poseer un UUID o Universal Unique Identifier o Todo recurso en REST necesita un UUID para ser identificado dentro del sistema • La forma en que se represente internamente un recurso debe ser privada • Todos los recursos deben de poseer un interfaz de operaciones, y todos deben de implementarlas.
  8. Cómo funciona? • Las operaciones se realizan por transferencia de

    estado o El cliente pide una copia del recurso al servicio o El cliente modifica el recurso o El cliente transfiere el recurso actualizado al servicio • Los recursos deben ser multimedia o Los recursos deben de poder expresarse en más de un formato • JSON, XML, CSV… o En las peticiones podemos y debemos indicar el tipo de formato que admitimos, o una lista con varios, ordenada por prioridades. • Según la implementación que se consiga, un API se cataloga en uno de los niveles REST
  9. Verbos HTTP • El protocolo HTTP define un conjunto de

    verbos que nos permiten realizar todo tipo de operaciones: o GET (LEER) o PUT (CREAR O ACTUALIZAR RECURSO COMPLETO) o POST (CREAR, ACTUALIZAR PARCIALMENTE UN RECURSO) o PATCH (ACTUALIZAR PARCIALMENTE UN RECURSO) o DELETE (BORRAR) o OPTIONS (CONSULTAR OPERACIONES) • http://www.restapitutorial.com/lessons/httpmethods.html • Usarlos incorrectamente lleva a desastres como el de Basecamp. o http://www.mail-archive.com/[email protected]/msg04213.html
  10. POST e idempotencia • POST es él verbo “maldito” por

    REST, al no ser idempotente. • La idempotencia asegura que ejecutar n veces una transferencia obtendrá el mismo resultado. • Se usa para modelar operaciones como: o Crear recursos o Modificar parcialmente recursos…
  11. Códigos de Respuesta • El resultado de una transferencia REST

    viene dado por el código de estado de la respuesta HTTP, en REST, los más habituales son: o 200 (OK) o 201 (Creado) o 202 (Aceptado) o 204 (Sin contenido) o 304 (No modificado) o 400 (Petición mal formada) o 401 (No autorizado) o 403 (Acceso no permitido) o 404 (No encontrado) o 409 (Conflicto) o 412 (Fallo de pre-condición) o 500 (Problema de Servidor) • http://www.restapitutorial.com/httpstatuscodes.html
  12. Ejemplos En nuestra API de pruebas usaremos: • GET o

    Obtener recursos o colecciones de recursos • POST o Creación de recursos • PATCH o Actualizaciones parciales • PUT o Actualizaciones totales de recursos • DELETE o Eliminar recursos • OPTIONS o Consultar acciones disponibles La URL sobre la que trabajaremos será: http://symfonyvalencia.es/api/usuarios El servidor puede devolver datos en XML o en JSON
  13. Transferencia GET 
 Obtener una Colección Cliente Servidor GET /api/usuarios

    HTTP/1.1 Host: www.symfonyvalencia.es Accept: application/json
  14. Transferencia GET 
 Obtener una Colección Cliente Servidor GET /api/usuarios

    HTTP/1.1 Host: www.symfonyvalencia.es Accept: application/json HTTP/1.1 200 OK Content-Type: application/json [{ “id”: “X1B4Z3”, “nombre”: “Miguel Ángel”, “edad”: 27, “lenguajes”: [“PHP”, “Javascript”] }, { “id”: “AB325J”, “nombre”: “Ángel Miguel”, “edad”: 26, “lenguajes”: [“Ruby”, “Java”] }]
  15. Transferencia GET
 Obtener un registro Cliente Servidor GET /api/usuarios/X1B4Z3 HTTP/1.1

    Host: www.symfonyvalencia.es Accept: application/json HTTP/1.1 200 OK Content-Type: application/json Etag: W/2cc7-3f3ba34cc25d { “id”: “X1B4Z3”, “nombre”: “Miguel Ángel”, “edad”: 27, “lenguajes”: [“PHP”, “Javascript”] }
  16. Transferencia POST
 Crear un recurso Cliente Servidor POST /api/usuarios HTTP/1.1

    Host: www.symfonyvalencia.es Content-Type: application/x-www-form- urlencoded Accept: application/json nombre=Manolo&edad=26&localidad= Valencia
  17. Transferencia POST
 Crear un recurso Cliente Servidor POST /api/usuarios HTTP/1.1

    Host: www.symfonyvalencia.es Content-Type: application/x-www-form- urlencoded Accept: application/json nombre=Manolo&edad=26&localidad= Valencia HTTP/1.1 201 Created Content-Type: application/json Location: http://symfonyvalencia.es/ usuarios/B2K1E3 Etag: W/2cc7-3f3ba34cc25d { ”id”: “B2K1E3” }
  18. Transferencia PUT
 Actualizar un recurso Cliente Servidor PUT /api/usuarios/X1B4Z3 Host:

    www.symfonyvalencia.es Content-Type: application/json Accept: application/json { “nombre”: “Miguel Ángel”, “edad”: 27, “lenguajes”: [‘PHP’, ‘Javascript’], “localidad”: “Valencia” }
  19. Transferencia PUT
 Actualizar un recurso Cliente Servidor PUT /api/usuarios/X1B4Z3 Host:

    www.symfonyvalencia.es Content-Type: application/json Accept: application/json { “nombre”: “Miguel Ángel”, “edad”: 27, “lenguajes”: [‘PHP’, ‘Javascript’], “localidad”: “Valencia” } HTTP/1.1 204 No Content Content-Type: application/json Etag: W/2cc7-3f3ba34cc25d
  20. API’S CRUD Las API’s CRUD son el tipo de API

    más extendido en el mundo REST
  21. APIs CRUD • Son las APIs más habituales • CRUD

    o Create (crear) o Read (leer) o Update (actualizar) o Delete (borrar) • Cubren las operaciones básicas que realizamos habitualmente sobre entidades • Implementan todos los verbos HTTP • Son lo contrario a APIs read-only • Son bastante sencillas de implementar • En Symfony2 existen bundles que facilitan su implementación, como FOSRESTBundle
  22. APIs CRUD • Si pretendemos ceñirnos absolutamente al concepto de

    API REST, no debemos de diseñar nuestra API como un reflejo de las entidades, ya que expone nuestro esquema de base de datos. • Un cambio en una entidad no debería de afectar al uso del API. • Se trata de modelar nuestro negocio con operaciones sobre recursos no de crear un interfaz para gestionar nuestra base de datos.
  23. Acceso concurrente • Si a nuestro Web Service se conectan

    simultáneamente varias personas y modifican el mismo registro… ¿Puede haber problemas de inconsistencia? • El problema de la inconsistencia en REST se soluciona habitualmente con la inclusión de cabeceras Etag y If-Match. • Se utilizan Etags débiles, ya que las fuertes son demasiado estrictas para nuestro propósito
  24. Ejemplo 1/3 Cliente Servidor GET /api/usuarios/X1B4Z3 HTTP/1.1 Host: www.symfonyvalencia.es Accept:

    application/json HTTP/1.1 200 OK Content-Type: application/ json;charset=utf-8 Etag: W/689438a788bbc2e { “id”: “X1B4Z3”, “nombre”: “Miguel Ángel”, “edad”: 27, “lenguajes”: [“PHP”, “Javascript”] }
  25. Ejemplo 2/3 Cliente Servidor (Match OK) PUT /api/usuarios/X1B4Z3 HTTP/1.1 Host:

    www.symfonyvalencia.es Accept: application/json If-Match: W/689438a788bbc2e Content-Type: application/json { “id”: “X1B4Z3”, “nombre”: “Miguel Ángel Sánchez”, “edad”: 23, “lenguajes”: [“PHP”, “Javascript”], “localidad”: “Valencia” } HTTP/1.1 204 No Content Etag: W/91246ef669ab7
  26. Ejemplo 3/3 Cliente Servidor (Match KO) PUT /api/usuarios/X1B4Z3 HTTP/1.1 Host:

    www.symfonyvalencia.es Accept: application/json If-Match: W/689438a788bbc2e Content-Type: application/json { “id”: “X1B4Z3”, “nombre”: “Miguel Ángel Sánchez”, “edad”: 23, “lenguajes”: [“PHP”, “Javascript”], “localidad”: “Valencia” } HTTP/1.1 412 Precondition Failed Etag: W/91246ef669ab7
  27. Actualizaciones parciales • Otro problema básico en REST es la

    actualización parcial del contenido. • Si usamos PUT, sobrescribimos todo el recurso, ¿y si solo queremos cambiar un atributo del recurso? • El método habitual se basa en crear nuevos tipos MIME de cambio de estado o diff y usar POST. • La solución moderna pasa por usar el verbo PATCH, funciona igual que con POST, pero es más semántico y solo admite tipos MIME que representen diffs
  28. Actualizaciones parciales • Cuando hablamos de diffs hablamos de crear

    un formato o tipo MIME que represente una operación de actualización parcial sobre un registro. • Suelen nombrarse como: o application/recurso-diff+formato • Recurso es el tipo de entidad (usuario, factura…) • Formato puede ser cualquier formato (json, xml…) • Ejemplos: o application/usuario-diff+json o application/usuario-diff+xml
  29. Ejemplo de actualización PATCH /api/usuarios/X1B4Z3 HTTP/1.1 Host: www.symfonyvalencia.es Accept: application/json

    If-Match: W/979c576c5be5fa5 Content-Type: application/usuario-diff+json [{ “tipo-cambio”: “sustituir”, “campo”: “edad”, “valor”: “23” }, { “tipo-cambio”: “anadir”, “campo”: “localidad”, “valor”: “Valencia” }]
  30. Operaciones de larga duración o asíncronas Cuando las operaciones no

    son inmediatas, o deben ser encoladas porque se procesan en bloque a cierta hora mediante un cron, o necesitan validación de un servicio externo (pasarelas de pago, Paypal…), el usuario debe de poder seguir el estado del recurso. Con REST es posible modelar operaciones asíncronas o encolables creando un recurso intermedio que represente el estado del recurso antes de finalizar su creación.
  31. Ejemplo • Vamos a realizar un pago a una tienda

    online vía REST • El pago debe de ser validado por la entidad bancaria que implementa la pasarela de pago. • El proceso puede tardar entre 3 y 5min • El cliente desea saber cuando ha sido validado el pago
  32. Ejemplo 1/4 Cliente Servidor POST /api/pago HTTP/1.1 Host: www.symfonyvalencia.es Accept:

    application/json Content-Type: application/json { “metodo”: “visa”, “numtarjeta”: 1234567812345678, “cvv”: 123 “caducidad”: “12/17” “cantidad”: 2500 } HTTP/1.1 202 Accepted Location: /api/cola-pago/XBC3719 { “location”: “/api/cola-pago/XBC3719”, “estado”: “pendiente” }
  33. Ejemplo 2/4 Cliente Servidor GET /api/cola-pago/XBC3719 HTTP/1.1 Host: www.symfonyvalencia.es Accept:

    application/json HTTP/1.1 200 OK Content-Type: application/json { “estado”: “pendiente” }
  34. Ejemplo 3/4 Cliente Servidor (Pago OK) GET /api/cola-pago/XBC3719 HTTP/1.1 Host:

    www.symfonyvalencia.es Accept: application/json HTTP/1.1 200 OK Content-Type: application/json { “estado”: “ok”, “id”: “/api/pago/DEB743AC” }
  35. Ejemplo 3 bis/4 Cliente Servidor (Pago OK) GET /api/pago/DEB743AC HTTP/1.1

    Host: www.symfonyvalencia.es Accept: application/json HTTP/1.1 200 OK Content-Type: application/json { “metodo”: “visa”, “numtarjeta”: 1234567812345678, “cvv”: 123 “caducidad”: “12/17” “cantidad”: 2500 “estado”: “pagado” }
  36. Ejemplo 4/4 Cliente Servidor (Error de pago) GET /api/cola-pago/XBC3719 HTTP/1.1

    Host: www.symfonyvalencia.es Accept: application/json HTTP/1.1 200 OK Content-Type: application/json { “estado”: “error”, “error”: “sin fondos }
  37. Seguridad en REST • En el contexto de un servicio

    REST no existe el concepto de Sesión, ya que HTTP es un protocolo Stateless no orientado a conexión. • Si nuestra API necesita autenticar a sus usuarios, estos deben de hacerlo en cada petición • Esto simplifica un ataque por parte de intrusos • Veamos algunas normas básicas de seguridad en REST
  38. 1. No uses IDs predecibles • Cualquier intruso que sniffe

    nuestro tráfico y detecte una petición del tipo: GET /api/usuario/1/datos-bancarios • Se verá tentado de probar con: GET /api/usuario/2/datos-bancarios GET /api/usuario/3/datos-bancarios GET /api/usuario/n/datos-bancarios • Tampoco debemos usar las claves primarias de nuestra base de datos, nos exponemos a ataques SQLi
  39. 2. Usa HTTPS • Con el protocolo HTTPS ya tenemos

    ganado la mitad • Es un protocolo seguro y el contenido va cifrado, tanto para peticiones como para respuestas. • Si transmitimos información sensible no será fácilmente reproducible por un atacante que haya usado un sniffer • Nos da seguridad a la hora de transmitir contraseñas
  40. 3. Genera Tokens de seguridad • Si creamos nuestro propio

    esquema de seguridad, generando Tokens que caduquen cada cierto tiempo, evitaremos transmitir la contraseña demasiadas veces. • Después de la primera autenticación, el servidor nos transmite nuestro Token • En las próximas peticiones lo incorporaremos en una cabecera, o una cookie
  41. 4. Algoritmo HMAC • Nos puede ayudar a construir buenos

    Tokens PARTE_PUBLICA = UUID + “:” + NIVEL_SEGURIDAD + ”:” + TIMESTAMP FIRMA = HMAC(CLAVE_SECRETA, PARTE_PUBLICA) TOKEN = FIRMA + ”_” + PARTE_PUBLICA • Desde el servidor basta con descomponer el Token en las componentes FIRMA y PARTE_PUBLICA, volver a calcular la firma con la PARTE_PUBLICA recibida y comparar la firma generada con la recibida
  42. 4. Algoritmos ajustables • Algoritmos como BCRYPT o PBKDF aceptan

    un parámetro de “complejidad”, que permiten indicar cuanto tardará en calcularse la contraseña. • Esto limita los ataques de fuerza bruta, por ejemplo, con una complejidad de 500ms solo podrían probarse 2 passwords por segundo.
  43. 5. Usa OAuth • Una opción alternativa a generar nuestros

    tokens es implementar un servidor OAuth que nos permita acceder a nuestra API • Existen librerías en casi todos los lenguajes que implementan OAuth • Podemos incluso usar nuestros credenciales en redes sociales para acceder a nuestra API.
  44. Testing y pruebas • Como cualquier otro programa, aplicación web…

    que desarrollemos, las APIs REST se pueden implementar usando TDD. • Es importante testear siempre que todos los códigos de respuesta devueltos son adecuados, tanto para casos de éxito como para casos de error. • IMPORTANTE: Muchos frameworks implementan listeners de excepciones y evitan, por ejemplo que una página devuelva un 404, enmascarando el resultado con una respuesta 200.
  45. Testing con xUnit • Podemos usar tranquilamente nuestro framework de

    pruebas xUnit para testear, no solo los códigos de respuesta, sino toda la lógica de negocio. • Las prácticas habituales de testing con este tipo de herramientas son totalmente válidas para el testeo.
  46. Testing con cURL • Es una alternativa para realizar nuestros

    tests (cURL + Shell Script) • Con cURL podemos crear exactamente la transferencia que buscamos. • Si se tienen suficientes conocimientos sobre cURL se pueden crear buenos tests con un tiempo de ejecución bastante bajo.
  47. Testing con Guzzle (PHP) • Guzzle es una librería en

    PHP que actúa como un wrapper de cURL, exprimiendo al máximo su potencia. • Permite crear clientes REST muy potentes y escalables • Es realmente sencillo de utilizar y es orientado a objetos • Se integra bien con PHPUnit.
  48. Testing con POSTMAN • POSTMAN es un plugin para Chrome

    que actúa como cliente REST • Nos permite guardar las transferencias preconfiguradas • Permite autenticación mediante OAuth de forma bastante sencilla • No es una herramienta automática (hay que ejecutar manualmente cada test y comprobar su resultado), por lo que no es recomendable testear sólo con POSTMAN
  49. Documentación con Swagger • Swagger es un estándar para definición

    de APIs REST y un framework que nos permite visualizar su representación y también consumir el API. • Es una herramienta muy útil para publicar nuestra API y que todo el mundo aprenda a usarla rápidamente. • Existe un bundle para Symfony2 que implementa Swagger: NelmioApiDocBundle