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.
La computación distribuida se centraba en sistemas RPC (Remote Procedure Call).
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 (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...)
WS-Security:
WS-AtomicTransaction
WS-ReliableMessaging
Tipado fuerte: Hace que la interoperabilidad entre sistemas sea algo complejo.
XML: Es lento de parsear
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).
(o por qué defender una implementación REST frente a soluciones RPC tradicionales)
Fácil escalabilidad de evolución de sus componentes
Los grandes lo utilizan (adaptadas a sus particularidades):
(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
Cada mensaje deberá contener toda la información sobre como manipular el mensaje
mimetype
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.
Solo se cachearán los contenidos que se auto identifiquen como cacheables.
Se basará en el sistema de cache HTTP:
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
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.
Desde un punto de visto REST, una operación idempotente es aquella que pese a ser invocada varias veces, produce siempre el mismo resultado.
TRACE
DELETE (Aunque no cambie el estado del servidor, la respuesta no es idéntica)
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
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
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
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.
PUT http://www.example.com/clientes/12345 PUT http://www.example.com/clientes/12345/pedidos/98765 PUT http://www.example.com/presupuestos/1234
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".
DELETE http://www.example.com/clientes/12345 DELETE http://www.example.com/clientes/12345/pedidos
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
200 OK
201 Created
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
No es necesario que los modelos internos de la aplicación mapeen 1-1 con los recursos.
Soporte para jerarquía:
GET http://api.example.com/services?op=actualizar_cliente&id=12345&format=json
GET http://api.example.com/actualizar_cliente/12345
GET http://api.example.com/clientes/12345/actualizar
PUT http://api.example.com/clientes/12345/actualizar
La acción esta especificado 2 veces
¡¡¡ANTIPATRONES A EVITAR!!!
* /configuracion * /clientes/12345/configuracion
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
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' } }
El objetivo es mantener la URL simple y entendible.
Propuesta: Utilizar el nombre del campo como parámetro;
GET /pedidos/?estado=enviados
GET /pedidos/ganados_semana
GET /pedidos/?orden=-date,importe
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>; rel="next", <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"
El cacheo se delega 100% en el protocolo HTTP.
Cualquier sistema de cache HTTP, es válido para ser implementado en una API REST.
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.
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.
{ 'id':'12', 'ciudad':'bilbao' }
metodo_callback({ status_code: 200, next_page: "https://..", response: { 'id':'12', 'ciudad':'bilbao' } });
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.
Se ahorra un 60% de ancho de banda en la respuesta
La seguridad no es un estado, es una medida.
Algunas buenas prácticas:
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.
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.
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.
GET / HTTP/1.1 Host: example.org Authorization: Basic Zm9vOmJhcg==
CONTRASEÑA NO CIFRADA
USAR SOLO SOBRE SSL
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
La base de OAuth es crear token temporales para autenticar.
Oauth2 delega en un tercero la autenticación.
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.
Una API es tan buena como su documentación
Una vez se publica una API pública, existe un compromiso de mantener la funcionalidad durante un largo periodo de tiempo.