slides-restfull



slides-restfull

1 2


slides-restfull


On Github irontec / slides-restfull

REST

La Transferencia de Estado Representacional (Representational State Transfer) o REST es una técnica de arquitectura software para sistemas hipermedia distribuidos como la World Wide Web.

(Wikipedia)

  • Término acuñado en la tesis doctoral de Roy Fielding (2000).
  • Estándar de facto para la interoperabilidad entre sistemas.

Antes de REST

La computación distribuida se centraba en sistemas RPC (Remote Procedure Call).

  • Funcionar de manera similar a un sistema local.
  • Servicios Web interoperables de manera transparente.
  • Servicios Web compatibles entre distintas plataformas.

8 mentiras en computación distribuida

Al construir un sistema distribuido, prácticamente todo el mundo, da por hecho las siguientes ocho asumciones. Todas son falsas a la larga y causarán problemas y dolorosas experiencias de aprendizaje.

Peter Deutsch (1994)

La red es confiable. La latencia es 0. El ancho de banda es infinito. La red es segura La topología de red no cambiará. Sólo hay un único administrador. El coste del transporte es 0. La red es homogénea.

TODO MENTIRA

SOAP

SOAP (Simple Object Access Protocol) es un protocolo estándar que define cómo dos objetos en diferentes procesos pueden comunicarse por medio de intercambio de datos XML (únicamente).

  • Con SOAP se exponen fragmentos de lógicas de negocio en forma de métodos definidos en un WSDL(Web Services Description Languages)

  • Es 100% independiente de la capa de transporte, por lo que puede usarse a través de cualquier protocolo que transmita texto (HTTP, SMTP, FTP...)

SOAP

Ventajas

  • WS-Security:

    • Implementación que añade funcionalidades de seguridad extra (garantía de integridad y privacidad)
    • Comunicación por SSL (como REST)
  • WS-AtomicTransaction

    • Soporta transacciones ACID
      • (Atomicidad, Consistencia, Aislamiento, Durabilidad)
  • WS-ReliableMessaging

    • Implementación de una lógica de reintentos para garantizar la comunicación punto a punto entre mensajes.

SOAP

Desventajas

  • Tipado fuerte: Hace que la interoperabilidad entre sistemas sea algo complejo.

  • XML: Es lento de parsear

Qué NO es REST

  • Un lenguaje de programación
  • Un framework (aunque hay frameworks para el desarrollo de APIs)
  • Una tecnología concreta
  • Una especificación

¿Qué es REST?

REST es un estilo de arquitectura para diseñar una API.

Son un conjunto de recomendaciones que tienen como objetivo hacer que la API sea lo más usable posible.

(Diseñar una API no difiere de diseñar una Interfaz de Usuario; debería ser coherente y fácil de usar).

Ventajas REST

(o por qué defender una implementación REST frente a soluciones RPC tradicionales)

  • La arquitectua se simplifica (rendimiento)
  • Peticiones se simplifican (velocidad)
  • Interoperabilidad real entre distintas plataformas
  • Curva de aprendizaje inexistente
  • Resultados visualmente interpretables
  • Fácil escalabilidad de evolución de sus componentes

  • Los grandes lo utilizan (adaptadas a sus particularidades):

Los 6 principios de REST

(6 constraints)

Un sistema es REST cuando intenta implementar la mayoría de los 6 principios.

(Será RESTFULL si llega a implementar los 5 primeros).

Interfaz Uniforme

Sin Estado

Cacheable

Cliente-Servidor

Sistema Capeado

Código Bajo Demanda

1 - Interfaz Uniforme

Basado en recursos

  • Cada Recurso único será identificada basado en una URI
  • La URI devolverá una representación del recurso basándose en las caracteristicas de la petición (JSON/XML por ejemplo)

1 - Interfaz Uniforme

Recursos Manipulables

  • Cuando el cliente tiene el la representación de un recurso, éste posee toda la información para modificarlo o borrarlo.

1 - Interfaz Uniforme

Mensajes Descriptivos

  • Cada mensaje deberá contener toda la información sobre como manipular el mensaje

  • mimetype

  • politicas de cache
  • etc...

1 - Interfaz Uniforme

HATEOS (Hypermedia as The Engine os Aplication State)

  • Petición: URI + headers + QueryString + Body
  • Respuesta: Code + headers + body
  • La respuesta puede incluir enlaces a otros recursos relacionados.
  • Permite al servidor evolucionar independientemente del cliente.

2 - Sin Estado

  • En un claro paralelismo a HTTP, una petición REST no mantendrá un estado en el servidor. La petición no dependerá de ninguna otra petición anterior.

  • Toda la información necesaria para servir una petición, debe estar incluida en la petición.

Supone una gran ventaja a la hora de escalar y/o balancear carga.

3 - Cacheable

  • Un cliente REST debe ser capaz de cachear ciertos contenidos.
  • Solo se cachearán los contenidos que se auto identifiquen como cacheables.

  • Se basará en el sistema de cache HTTP:

    • caducable
    • validable

4 - Cliente - Servidor

  • El cliente no sabe nada de como se almacena los datos.
  • El servidor no conoce ni el estado del cliente, ni nada relacionado con la UI.

Mientras la interfaz se mantenga inalterable, se podrán modificar tanto el cliente como el servidor de manera independiente.

5 - Sistema capeado

  • Un cliente no podrá detectar si se encuntra conectado a un servidor final o a un proxy intermedio.
  • Introducir agentes intermedios puede aportar diversas ventajas:
    • caches intermedias
    • políticas de seguridad
    • balanceo de carga transparente para el cliente

6 - Codigo bajo demanda

  • Este principio permitiría al cliente solicitar código (Applet de Java, Javascript, etc), para ejecutarlo bajo demanda.

  • Es el único principio no obligatorio para definir una API como RESTFULL

Verbos

  • Son la parte en la API REST que define que acciones se van a llevar a cabo sobre los recursos

  • Para ello se van a utilizar métodos HTTP que definan semánticamente la acción a realizar para con el recurso.

    • GET: Recupera un recurso o listado de recursos
    • POST: Crea un recurso nuevo
    • PUT: Actualiza un recurso
    • PATCH: Actualiza parcialmente un recurso
    • DELETE: Elimina un recurso

Idempotencia

  • Desde un punto de visto REST, una operación idempotente es aquella que pese a ser invocada varias veces, produce siempre el mismo resultado.

    • GET
    • HEAD
    • OPTIONS
    • TRACE

    • DELETE (Aunque no cambie el estado del servidor, la respuesta no es idéntica)

    • PUT (Depende de la implementación)

GET

  • Se utiliza para leer uno o varias reprsentaciones de recursos.

  • No debería cambiar el estado de ningún recurso en el servidor

  • Podrá ser invocada 100 veces y siempre se conseguriá la misma representación (idempotente)

GET http://www.example.com/clientes/12345

GET http://www.example.com/clientes/12345/pedidos

GET http://www.example.com/presupuestos

POST

  • Verbo utilizado para crear nuevos recursos

  • La respuesta podría ser un 201 (created), y una cabecera de redirección hacia el nuevo recurso

  • No es idempotente, ni seguro. Si es invocado 2 veces, seguramente se crearán dos recursos con la misma información.

POST http://www.example.com/clientes
POST http://www.example.com/clientes/12345/pedido

PUT

  • Su utilidad más extendida será la de actualizar un recurso concreto.

  • Podría usarse para crear entidades en un sistema donde el ID es escogido por el usuario

    • No es muy recomendable por raro
  • La respuesta podría ser un 200 ó un 204. No es necesario una redirección ya que el usuario ya conoce el ID del recurso.

  • No es una operación segura (modifica el estado de un recurso)

  • Puede considerarse idempotente.

    • (a no ser que se agrege un contador o se modifique una fecha)
PUT http://www.example.com/clientes/12345
PUT http://www.example.com/clientes/12345/pedidos/98765
PUT http://www.example.com/presupuestos/1234

DELETE

  • Se utiliza para eliminar uno varios recursos
  • No es seguro ya que cambia el estado del servidor.

  • La respuesta más habitual sería un 204 (No data).

  • DELETE se considera idempotente en el caso que la API marque los recursos como "borrados".

    • Si se realiza un borrado lógico, se responsería un 404; por lo que dejaría de ser un método idempotente.
DELETE http://www.example.com/clientes/12345
DELETE http://www.example.com/clientes/12345/pedidos

Codigos de Respuesta

  • Se utilizan códigos de respuesta HTTP estándar para indicar el resultado de una acción.

  • Se deberá buscar el código de respuesta más relevante a nuestra acción concreta

20x

  • 200 OK

    • Lo común es acompañar la respuesta con la representación del recurso.
  • 201 Created

    • Nueva entidad creada
    • El cuerpo de la respuesta podría contener una representación del recurso.
    • Se podría incluir una cabecera de redirección hacia la representación del recurso.
  • 204 No Content

    • No existe necesidad de devolver contenido (DELETE)

30x

  • 304 Not Modified
    • Perfecto para validar cache (ETag / Expires)

40x

  • 400 Bad Request

    • Petición no entendida
  • 401 Unauthorized

    • Petición sin credenciales
  • 403 Forbidden

    • No tienes permitido acceder a este contenido.
  • 404 Not Found

    • Sigue buscando
  • 409 Conflict

    • Existe algún conflicto con el estado del recurso.
    • Debiera ser un conflicto resolvible por el usuario
    • (clave única duplicada por ejemplo)

50X

  • 500 Internal Server Error
    • No debería ser lanzado intencionalmente.
    • Tpicamente lanzado desde el catch general de la aplicacion.

¿Qué es un recurso?

  • Siempre un sustantivo
  • Nunca un verbo.
  • No es necesario que los modelos internos de la aplicación mapeen 1-1 con los recursos.

  • Soporte para jerarquía:

    • /clientes/12/pedidos

Nombrando recursos

  • Nombres descriptivos
  • Fácilmente relacionables entre ellos para crear URLs anidadas.
  • La URL debe tener sentido.
    • La API se diseña para el usuario consumidor de la API, no para mantener consistencia en tus datos.

Anti Patrones para Recursos

Pluralización

  • Utiliza SIEMPRE plurales para describir recursos
  • Es la única manera de mantener el nombre consistente con todos los métodos HTTP.

Existe una excepción con recursos únicos; con entidades que implementarían un patrón singleton. Este tipo de entidades nunca llevarán un ID asociado.

* /configuracion
* /clientes/12345/configuracion

Respuestas

  • La respuesta a cualquier petición a una API RESTFULL, en caso de tener contenido, será:
    • Una colección de representaciones de recursos
    • Una representación de un recurso
    • (Y una colección de enlaces si implementa HATEOAS, cosa no muy común)

Formatos

  • Lo ideal es que una API soporte XML y JSON.
  • Que el usuario escoja en la petición en que formato quiere la representación
    • Cabecera HTTP Accept
    • añadiedno .json / .xml a la petición

Formato JSON

  • Debería ser la primera opción.
  • Simple.
  • Alta Legibilidad.
  • Rápido.

Formato XML

  • Más costoso de parsear
  • Su lectura puede complicarse
  • Lo ideal sería minimizar el numero de nodos anidados.
  • Puede ser requisitos en entornos con alta especificación.

Limitar campos

(buena práctica)

  • Puede optimizar el uso de la red, ya que no siempre será necesaria recuperar una representación exacta del recurso:

  • Propuesta: Utilizar el parámetro fields para especificar que campos se quiere recuperar en la representación.

GET /pedidos?fields=id,date,amount

Limitar Relaciones

(buena práctica)

  • Es bastante común que al recuperar un recurso, el usuario necesite recuperar otros recursos relacionados.
  • Aunque no sería RESTFULL, minimizamos el número de peticiones.

  • Propuesta: Utilizar el parámetro "expand"

GET /pedidos/12345?exand=cliente.nombre,cliente.email

La respuesta sería:

{
    'id':12345,
    'ammount':'34€',
    'date':'2015-04-12 13:33:33+01:00',
    'cliente' : {
        'nombre' : 'Jabi',
        'email': 'jabi@irontec.com'
    }
}

Filtros / Búsquedas

(buena práctica)

  • Es recomendable utlizar un único campo a la hora de filtrar una colección de recursos.
  • El objetivo es mantener la URL simple y entendible.

  • Propuesta: Utilizar el nombre del campo como parámetro;

    • (limitarlo a una lista blanca, y documentarlo)
GET /pedidos/?estado=enviados
  • Otra práctica recomendable crear alias que realicen búsquedas más complejas pero habituales
GET /pedidos/ganados_semana

Orden

(buena práctica)

  • Para solicitar una colección ordenada con respecto a uno o varios campos, de manera ascendiente o descendiente
  • Propuesta: Utilizar el parámetros "sort"
    • Separar por comas los campos a tener en cuenta para ordenar
    • Utilizar un signo negativo si la ordenación debe ser descendiente
GET /pedidos/?orden=-date,importe

Paginacion

(buena práctica)

  • Propuesta: Utilizar parámetro "page" para especificar

  • Existe un RFC que especifica como devolver información de paginación en la cabecera. (Enlace ietf)

Link: <https://api.github.com/user/repos?page=3&per_page=100&gt;; rel="next", <https://api.github.com/user/repos?page=50&per_page=100&gt;; rel="last"

Cacheo

  • El cacheo se delega 100% en el protocolo HTTP.

  • Cualquier sistema de cache HTTP, es válido para ser implementado en una API REST.

ETag

Cacheo

  • La API deberá incluir en la respuesta una cabecera ETag con un checksum del contenido que está devolviendo.

  • El cliente lo almacena asociado a la URI

  • En la próxima petición a ese mismo recurso, se añade a la petición la cabecera If-None-Match:

  • La API devolverá un 304 Not Modified si el valor coincide con la firma del recurso.

Last-Modified

Cacheo

  • La API deberá incluir en la respuesta una cabecera Last-Modified con la fecha de última modificación del recurso.

  • El cliente almacena la fecha asociado a la URI

  • En la próxima petición a ese mismo recurso, se añade a la petición la cabecera If-Modified-Since:

  • Si el contenido no ha sido modificado, la API devolverá un 304 Not Modified.

Evitar Envoltorio (Wrap)

(buena práctica)

  • Evitar cuando sea posible, envolver los datos en una estructura extra.
    • overhead innecesario
    • metadatos en cabeceras
{
    'id':'12',
    'ciudad':'bilbao'
}

Excepciones

  • JSONP: El cliente no soporta CORS
    • La petición viaja con un parámetro jsonp=metodo_callback
    • La API devolverá siempre un status Code 200
metodo_callback({
    status_code: 200,
    next_page: "https://..",
    response: {
        'id':'12',
        'ciudad':'bilbao'
    }
});

Response Pretty

  • JSON puede estár minificado y ahorrar espacio:

    {'id':12345,'ammount':'34€','date':'2015-04-12 13:33:33+01:00','cliente' : {'nombre' : 'Jabi','email':'jabi@irontec.com'}}
  • O ser bonito y leible:

    {
      'id':12345,
      'ammount':'34€',
      'date':'2015-04-12 13:33:33+01:00',
      'cliente' : {
          'nombre' : 'Jabi',
          'email': 'jabi@irontec.com'
      }
    }
  • Siempre que el contenido esté comprimido a nivel HTTP, es más usable la segunda opción, ya que lo hace mucho más accesible y fácilmente consumible.

Gzip

  • Al utilizar HTTP, es muy muy fácil utilizar gzip para comprimir las respuestas.

Se ahorra un 60% de ancho de banda en la respuesta

Seguridad

  • La seguridad no es un estado, es una medida.

    • No existe algo seguro o inseguro... todo depende de que se quiera securizar.
  • Algunas buenas prácticas:

    • No incluir apiKey en la URL
    • Utilizar listas blancas de método permitidos
    • Comprobar los datos de entrada (strong typing)

(Enlace Recomendaciones OWASP)

SSL

  • Utilizar SSL siempre.

  • Facilita el sistema de autenticación, ya que se delega el cifrado de las credenciales en la capa de transporte.

  • No redirigir desde HTTP a HTTPs; es mejor mostrar un error, y que NO funcione.

    • De otra manera, las peticiones HTTP SI funcionarían y viajarían (al menos una vez), en claro.

CORS

Cross-Origin Resource Sharing

  • Mecanismo que permite a un servicio REST, validar el origen de una petición.

  • En la petición, el navegador enviará:

    Origin: http://www.foo.com
  • El servidor deberá enviar:

    Access-Control-Allow-Origin: http://www.foo.com

    En caso contrario, la petición no tendrá éxito.

Autenticación

  • Basándonos en el principio 2 de REST, STATELESS, cada petición a la API, deberá contener las credenciales autenticadas.

  • La autenticación viaja generalmente en la cabecera Authorization de la petición.

  • Existen multitud de posibilidades para autenticar cada petición.

  • Deberemos implementar la que satisfaga nuestra necesidad de seguridad
    • ¿estamos siempre sobre SSL?
    • ¿tenemos usuario públicos?
    • ¿estamos en una intranet?

Basic Digest

Autenticación

  • Se crea un simple hash base64 con "usuario:contraseña"
GET / HTTP/1.1
Host: example.org
Authorization: Basic Zm9vOmJhcg==

CONTRASEÑA NO CIFRADA

USAR SOLO SOBRE SSL

HMAC

Autenticación

  • hash based message authentication
  • La técnica consiste en crear un hash en base a un secreto compartido
  • Es una buena práctica incluir un dato variable, como la fecha (NONCE) o la URL
digest = base64encode(hmac("sha256", "secret", "GET+/users/secretos+2014-04-11:12:32:21+989898"))
GET /users/secretos HTTP/1.1
Host: example.org
Authentication: hmac jabi:989898:[digest]
Date:  2014-04-11:12:32:21

No se envía la contraseña, si no un secreto compartido La petición tiene fecha de caducidad

OAUTH2

Autenticación

  • El problema con HMAC, es que se basa en un secreto compartido.
  • Si el secreto se descubre, el servicio queda expuesto
  • La base de OAuth es crear token temporales para autenticar.

  • Oauth2 delega en un tercero la autenticación.

  • El tercero debe permitir la integración.
  • El usuario debe permitir que el tercero valide su identidad en nuestro servicio.
  • OAuth2 se basa en la generación de un token de refresco, que generará tokens temporales.
  • El extremo del servidor validará cada petición contra el tercero.

  • El usuario puede revocar el permiso en la aplicación del tercero en cualquier momento.

Documentación

Una API es tan buena como su documentación

  • Se deberán mostrar ciclos completos de request/response.
  • Deberían existir ejemplos para poder copiar/pegar directamente.

Una vez se publica una API pública, existe un compromiso de mantener la funcionalidad durante un largo periodo de tiempo.

Fin!

Referencias