Tema 3.6: El Estilo Arquitectónico REST Índice Introducción Conceptos básicos de REST Recursos y Representaciones Conjunto fijo de operaciones Hypermedia: Cambio de estado Servicios auto-descriptivos Intermediarios y caches Ejemplo: Movies RPC vs Movies REST Estilo REST vs Estilo RPC Valoración y Conclusiones Introducción REpresentational State Transfer. Estilo arquitectónico propuesto por Roy Fielding en 2000. La Web es, sin duda, la aplicación distribuida más exitosa de la historia. REST: Estilo arquitectónico para construir aplicaciones distribuidas inspirado en las características de la web. REST se basa fuertemente en HTTP 1.1: Realmente es independiente de HTTP Pero HTTP 1.1 es el único protocolo utilizado masivamente diseñado para soportar los principios REST. Introducción (y 2) El estilo REST pretende que sea posible construir sistemas distribuidos compuestos por miles de servicios desarrollados y evolucionados independientemente, escalando a niveles comparables a la Web. Para ello hace énfasis en minimizar el acoplamiento entre servicios. A menudo se les denomina servicios web “RESTful”. Nosotros también los denominaremos servicios web REST “puros”. Recursos y Representaciones (1) Una aplicación REST se compone de recursos. Cada recurso es identificado mediante un identificador único (típicamente URLs): http://www.boeing.com/aircraft/747 Puede representar un 747 de la compañía Boeing http://www.movieprovider.com/movies/Amelie Amelie en la BD de películas de movieprovider.com. http://www.amazon.com/Ajax-REST-Recipes-Problem-SolutionApproach/dp/1590597346/ El libro “Ajax and REST Recipes: A Problem-Solution Approach” (ISBN: 1590597346) en Amazon. http://en.wikipedia.org/wiki/HTTP Representa la información sobre HTTP proporcionada por Wikipedia. Los identificadores (URLs) son globales. Todo recurso tiene un nombre único a nivel mundial. No existen espacios de nombres restringidos a un servicio/aplicación. Eso no quiere decir que todos los recursos sean accesibles para todos (mecanismos de autorización). Recursos y Representaciones (y2) Al invocar la URL, el cliente obtiene una representación del recurso. La representación de un recurso puede variar en el tiempo. El identificador está ligado al recurso, no a la representación. Si cambian los datos sobre HTTP en la Wikipedia, cambiará la representación. La URL apuntará siempre a la representación actual del recurso (entrada actual en Wikipedia). La representación puede permitir actuar sobre el recurso. Ejemplo: Formularios para introducir datos. La representación puede contener nuevas URLs hacia otros recursos. Ejemplo: cuando atravesamos un hiperenlace (URL) en la web estamos accediendo a un recurso cuya representación (página web) puede contener otros hiperenlaces. Conjunto fijo de operaciones(1) Interfaz uniforme. Las operaciones disponibles sobre los recursos son siempre las mismas y su semántica es conocida por todos los clientes y servicios. El estilo REST no dice cuáles deben ser esas operaciones, sólo que deben ser siempre las mismas y ser usadas consistentemente por los servicios. En el caso de HTTP: GET, POST, PUT, DELETE. GET: Acceso a recursos y consultas. Sin efectos secundarios. PUT: Crear o reemplazar completamente la representación de un recurso. Idempotente. DELETE: Borrar un recurso. Idempotente. POST: Acciones que pueden tener efectos secundarios (creación / modificación de recursos, coste) y pueden no ser idempotentes. Conjunto fijo de operaciones(y2) Analogía con QUERY / UPDATE / INSERT /DELETE en una Base de datos. PUT sirve para UPDATEs idempotentes e INSERTS (cliente decide el identificador). POST sirve para INSERT si servidor decide URL. Sirve para UPDATES no idempotentes. También se usa a menudo para UPDATES parciales. Un servicio web REST debe utilizar sólo estas operaciones para manipular los recursos. Esto quiere decir que cierta semántica de una invocación (“lo que hace”) sobre un recurso es conocida sin saber nada sobre el servicio. Facilita la interoperabilidad entre aplicaciones. Para manipular un recurso de otra aplicación sólo se precisa su URL. Este conocimiento semántico limitado es suficiente para permitir capas intermedias (e.g. cache) transparentes que proporcionan servicios útiles. Hypermedia: Cambio de Estado La representación recibida por el cliente cambia (transfer) su estado (state). Una aplicación REST (web) puede verse como el grafo de transición de estados de un autómata. Las representaciones de recursos (páginas) son estados del autómata. Las URLs (hiperenlaces) son transiciones entre estados. El estado en el que estamos determina qué otros estados (recursos) tenemos accesibles. No hay estado específico para cada cliente en el servidor: Cada petición del cliente debe incluir todo lo necesario para que el servidor pueda responderla. Facilita escalabilidad y replicación. Servicios Autodescriptivos (1) Idealmente, un cliente de un servicio REST necesitaría conocer a priori una única URL (la de entrada al servicio). El resto de sus interacciones serían guiadas por las representaciones por las que va navegando: Hiperenlaces para operar sobre otros recursos. Plantillas de consulta/actualización que permitan operar sobre otros recursos usando parámetros especificados por el cliente: Equivalente a formularios en la web. Permite que el servicio varíe los parámetros de la operación o la URL sobre la que actúan dinámicamente. Servicios Autodescriptivos (2) Las representaciones devueltas deben intentar expresarse en formatos conocidos a priori por todos los clientes posibles del servicio: Intentar evitar formatos propietarios de cada servicio. En lo posible, utilizar tipos MIME con semántica conocida (e.g. ATOM). Si los clientes posibles están dentro de una organización intentar usar estándares de la organización. Si hay que añadir información específica, intentar proporcionar también una representación en un formato estándar (e.g. ATOM) Así, cualquier cliente podrá al menos obtener parte de la información. Servicios Autodescriptivos (y 3) Si esto se consigue, el cliente y el servicio están muy desacoplados: El servicio puede cambiar totalmente su funcionamiento interno (e.g. esquemas de URL, campos consultables para un recurso) sin que el cliente rompa. Análogo a un usuario humano que accede con su browser a una web que ha cambiado totalmente -> aún eres capaz de usar el servicio. En muchos casos, puede ser muy difícil / imposible de conseguir totalmente: ¿Cómo expresar forms entendibles por programas?. Existen varias propuestas, aún no muy usadas (e.g. Xforms). No hay vocabularios estándar para todo ! Intentar ajustarse lo más posible o adoptar soluciones parciales (e.g. tener una representación en ATOM además de la representación en un formato específico) Intermediarios y Caches (1) Soporte para Cache: Las representaciones deben poderse marcar como cacheables o no por capas intermedias. Soporte para tiempos de expiración, detección de si hay o no nuevas versiones de un recurso,… El uso de una interfaz uniforme permite introducir intermediarios de forma transparente entre los clientes y los recursos: Servicios de eficiencia, seguridad,… Ejemplos : Proxies . Pueden implementar reintentos de forma transparente con peticiones idempotentes. Servidores de cache. Sabe que las peticiones GET de un recurso invalidan su copia, las otras sí. Filtros de contenido. Pueden decidir actuar sólo sobre peticiones GET. Intermediarios y Caches (2) La interfaz uniforme permite que los clientes también pueden realizar ciertos procesamientos sobre los recursos de un servicio sin conocimiento a priori: E.g. un crawler sabe que puede hacer GETS de representaciones. En un enfoque RPC típico, la semántica de una operación no es conocida para el intermediario/cliente a menos que se le configure específicamente para cada servicio. Si además de respetar la interfaz uniforme, se consigue que el sistema sea auto-descriptivo en un grado alto, los clientes / intermediarios pueden realizar operaciones más sofisticadas. Intermediarios y Caches (y 3) Intermediarios: Servicio-1 Cache HTTP Cliente HTTP HTTP Servicio-2 HTTP ... Servicio-N Ejemplo: Movies RPC (1) Ejemplo películas al estilo RPC (CORBA, DCOM, SOAP,…) Proveedor proporciona una interfaz que consiste en, entre otras, las siguientes operaciones: MovieSummary [] findMovies() MovieSummary [] findMoviesByDate(Calendar date) MovieInformationDetail getMovieInformation (String movieId) String addMovie(MovieInformationDetail info) void updateMovie (String movieId,MovieInformationDetail info) void removeMovie (String movieId) void addGross (double amount, movieId, Calendar week) NOTA: usamos notación “a la JAVA”. Ejemplo: Movies RPC (2) Los clientes del proveedor suelen utilizar: findMovies permite obtener la lista de todas las películas. findMoviesByDate permite obtener la cartelera de un día determinado. Ambos métodos devuelven la información más importante de una película (objeto MovieSummary): Se incluye un identificador numérico para cada película. No se devuelve toda la información porque puede ser bastante grande y normalmente no se necesita toda. Si se quiere toda la información de una película puede llamarse a getMovieInformation pasándole el identificador de la película. Ejemplo: Movies RPC (y 3) Las aplicaciones que editan los contenidos del proveedor (internas o externas) suelen utilizar addMovie permite añadir una película. removeMovie permite eliminar una película. updateMovie. Permite reemplazar la información completa de una película. addGross. Permite especificar la recaudación semanal de una película. Modifica el atributo recaudación total de la misma sumándole la nueva recaudación. No es idempotente. Las operaciones pueden lanzar exceptions ad-hoc como las que vimos en temas anteriores. Ejemplo: Movies REST (1) Cada película es un recurso con una URL asociada: http://www.moviesprovider.com/movies/Amelie http://www.moviesprovider.com/movies/The_Godfather_Part_II Su representación es un documento XML. Similar al ejemplo utilizado en el tema 2 con elementos de datos adicionales (e.g. recaudación, productora, puntero al trailer, punteros a biografías de los actores, …). Podría utilizarse una representación alternativa en ATOM (del estilo de la que vimos en el Tema 3.2) para los clientes que no entiendan nuestro formato: A través de cabeceras HTTP es posible para el cliente especificar qué formatos acepta. Podría no utilizarse XML: Si los datos son sencillos. Ejemplo: CSV. Si al cliente puede facilitarle su procesamiento. Ejemplo: desde clientes Javascript, es más fácil usar JSON. Ejemplo: Movies REST (2) http://www.moviesprovider.com/movies es un recurso colección que contiene: Su representación es un documento XML: La información resumida de todas las películas. Una referencia al recurso de cada película. Un “formulario” que indica como buscar por fecha. Incluye alguna información de resumen de cada película. Usa Xlink (http://www.w3.org/TR/xlink/) para especificar referencias a otros recursos. Usa una URI Template (http://bitworking.org/projects/URITemplates/spec/draft-gregorio-uritemplate-03.html) para especificar como consultar la colección por fecha. Podría haber una representación alternativa en ATOM para los clientes que no entendiesen nuestro formato (no serviría para la interfaz de consulta). Análogo a una página web convencional. Ejemplo: Movies REST (3) <?xml version="1.0"?> <mov:Movies xmlns:mov="http://www.movieprovider.com" xmlns:xlink= http://www.w3.org/1999/xlink xmlns:xf="http://www.w3.org/2002/xforms> <Movie title=“Amelie“ director=“Jean Pierre Jeunet” xlink:href="http://www.moviesprovider.com/movies/Amelie"/> <Movie title=“The Godfather Part II“ director=“Francis Ford Coppola” xlink:href="http://www.moviesprovider.com/movies/The_Godfather_Part_ II"/> <mov:Filter url=“http://www.example.com/?{-join|&|day,month,year}”> </mov:Movies> Ejemplo: Movies REST (4) http://www.movieprovider.com/movies puede recibir parámetros de “consulta”. http://www.movieprovider.com/movies?day=19&month=3&year= 2007 Pero los clientes no tienen que saber cuál es exactamente el formato de URL utilizado. El patrón de URL especifica esto. ¿Qué hacen los clientes ahora? Invocan http://www.moviesprovider.com/movies Parsean el XML para obtener la información resumida. Si quieren la información completa de una película, invocan a su referencia y parsean el XML. Si quieren la cartelera, interpretan la URI Template y componen la invocación con parámetros. Todas son peticiones GET. Ejemplo: Movies REST (5) ¿Qué hacen las aplicaciones que editan los contenidos ahora? Para añadir una película, realizan una petición PUT indicando la URL asociada Ejemplo: http://www.moviesprovider.com/Movies/Seven Los datos de la película van como un documento XML en el cuerpo del mensaje. Otra opción es hacer una petición POST a un recurso colección (http://www.movieprovider.com/movies) que nos devuelva la nueva URL: De esta forma es el servicio en lugar del cliente el que controla la generación de identificadores. Para borrar una película invocan el recurso que representa a la película con el método DELETE. Para reemplazar la información de una película invocan el recurso que representa a la película con el método PUT. Los datos de la película van como un documento XML en el cuerpo del mensaje. Ejemplo: Movies REST (6) Para la operación addGross hay varias opciones: El cliente obtiene el recurso (GET), lo modifica y lo sobreescribe (PUT). Definimos la recaudación como un recurso. Obliga al cliente a descargarse la representación completa. El cliente puede descargarlo y actualizarlo sin descargarse la representación completa de la película. Forzado, ya que la recaudación no parece realmente un recurso con entidad suficiente para ser referenciada por sí misma. Definimos un recurso http://www.moviesprovider.com/movies/XXXX/ al que accedemos vía POST: Recibe como parámetro la cantidad a incrementar y un indicador de la operación a realizar (e.g. addGross). Los intermediarios sabrán que es una operación no idempotente y con posibles efectos secundarios sobre el recurso, aunque no entiendan toda su semántica. Quizás la mejor opción. Ejemplo: Movies REST (y 7) En lugar de usar exceptions ad-hoc se utilizan los códigos de error de HTTP. De esta forma, clientes genéricos pueden interpretar los mensajes de error sin saber nada a priori sobre el servicio. HTTP, entre muchos otros, define códigos de error para situaciones tales como recurso duplicado o intentar eliminar un recurso no existente. Ejemplo: REST vs RPC (1) Espacio de nombres global vs local El enfoque RPC utiliza un espacio de nombres local basado en identificadores de película. Los identificadores del enfoque REST son globales. Si otro servicio web (e.g. que proporciona críticas de películas) quiere incluir una referencia a una película de movieprovider.com puede sencillamente incluir su URL en una de sus respuestas. Hace más fácil reusar recursos y servicios. El cliente debe aún conocer el vocabulario (formato XML) utilizado por movieprovider.com. Si existe una representación en un formato estándar como ATOM, un cliente genérico podrá aún hacer cierto uso del servicio sin conocer nuestro vocabulario específico. No existe una posibilidad equivalente en el enfoque RPC. Se usan identificadores numéricos no globales. Para acceder a un servicio siempre es necesario crear los stubs y los objetos asociados, que son propietarios. La causa principal del éxito de la Web no fueron HTML ni HTTP, cuya principal virtud era la simplicidad. La causa principal fue la utilización de un espacio de nombres global que permitía reusar muy fácilmente (mediante enlaces) los contenidos ya existentes. Ejemplo: REST vs RPC (2) Operaciones estándar vs Operaciones no estándar. RPC permite que cada servicio defina sus propias operaciones pero no expone ninguna semántica de las mismas. No puede saberse, por ejemplo, que: La operación addGross no es idempotente y además tiene efectos secundarios. La operación updateMovie es idempotente y tiene efectos secundarios. La operación findMovies es idempotente y no tiene efectos secundarios. REST intenta modelar aplicaciones en términos de recursos y usar un conjunto fijo de operaciones: GET, POST, PUT, DELETE. Cierta semántica de estas operaciones es conocida: POST tiene efectos secundarios y no tiene porque ser idempotente. PUT, DELETE tienen efectos secundarios pero son idempotentes. GET es idempotente y no tiene efectos secundarios. Ejemplo: REST vs RPC (3) Operaciones estándar vs Operaciones no estándar (cont.) Conocer algo sobre la semántica de las operaciones permite tener intermediarios/clientes que proporcionan valor añadido a CUALQUIER servicio (sin tener que saber nada sobre él a priori). Ejemplos: Un servidor de cache sabe que un recurso al que se haya emitido una petición PUT, DELETE o POST debe ser invalidado. Un proxy que, cuando una petición falla, la reintenta de forma transparente al cliente, sabe que puede reintentar una petición GET sin riesgo (porque es idempotente). Un crawler de recursos (“a la Google”) sabe que debe acceder sólo a recursos usando sólo GET (ausencia de efectos secundarios). Las invocaciones RPC no pueden beneficiarse directamente de los servicios de estos intermediarios, salvo que se configure específicamente para cada servicio. RPC permite tener el API más adecuado a las características de tu aplicación. REST obliga a modelar todas las aplicaciones de la misma forma, y puede no ser fácil o cómodo en todos los casos. Ejemplo: REST vs RPC (4) Servicios auto-descriptivos: Si se consigue, permite que un cliente pueda usar un servicio sin saber nada a priori sobre él. El cliente debe entender la forma de especificar las opciones. El servicio guía al cliente a través de las opciones posibles mediante hypermedia y especificaciones de formatos de consulta / actualización (e.g. URITemplates, Xforms) El uso de formatos estándares universales (e.g. ATOM y otros tipos MIME) permite que el cliente sepa interpretar los datos. Ejemplo: crawler / buscador de recursos accesibles a través de servicios web REST. No existen estándares suficientemente extendidos. No existen vocabularios estándar para todo. Intentar dar salida en algún formato estándar y complementar con formatos menos estándar para clientes con mayor conocimiento. Ejemplo: REST vs RPC (5) Accesibilidad REST usa directamente URLs y HTTP: Fácil ver y manipular datos para humanos: puedo acceder a un recurso directamente con un navegador (XML suele ser legible para humanos). Fácil devolver distintas representaciones del recurso. Ejemplo: HTML para usuarios humanos y XML para aplicaciones (cabecera HTTP Agente de Usuario + XSLT). Prácticamente cualquier aplicación (desde Office a los sistemas de integración de datos) puede acceder recursos HTTP. Interoperabilidad garantizada El cliente debe ocuparse de los detalles de ejecutar peticiones HTTP y parsear documentos XML Es bastante fácil en casi todos los lenguajes de programación. Ejemplo: REST vs RPC (6) Accesibilidad (cont.) RPC utiliza librerías y protocolos específicos: No es fácil para un humano acceder rápidamente a los recursos. Hay que construir aplicaciones ad-hoc para ello. Cualquier operación que involucre acceso al recurso implica programar. Para acceder a un servicio RPC, las aplicaciones deberán utilizar librerías generadas específicamente para nuestro servicio. Menor interoperabilidad. El programador no se preocupa de cómo se envían peticiones ni de parsear XML. El sistema RPC le da todo eso resuelto. El cliente pasa a ser dependiente de todo el XML de salida. Cualquier cambio en el mismo puede afectarle aunque sea en un campo que no usa. Puede haber problemas de interoperabilidad entre implementaciones (problema temporal). Ejemplo: Debido a bugs, un servicio SOAP programado con una toolkit puede tener problemas con otro servicio programado con otra toolkit. Ejemplo: REST vs RPC (7) Reusar mecanismos genéricos de diseño de HTTP vs Nuevos mecanismos Aunque REST es un estilo en teoría independiente de HTTP, este es el único protocolo actual que tiene las características necesarias para soportar este estilo. Con REST, pueden reusarse directamente los mecanismos de autorización (permisos de acceso), cifrado y autenticación de HTTP. Con esquemas RPC, deben crearse nuevos mecanismos para esto: En algunas tecnologías como CORBA ya existen. En el mundo SOAP no existen pero se están construyendo. Pueden crearse para que mejoren a los de HTTP. HTTP no tiene todo lo necesario (e.g. transacciones distribuidas, servicios de mensajería,…). Los defensores de REST proponen extender HTTP en lugar de crear nuevos protocolos. Ejemplo: REST vs RPC (y 8) Transporte fijo vs Independiente Transporte Con REST, en la práctica, el protocolo encargado de transferir las peticiones es siempre HTTP. Los sistemas RPC pueden utilizar diversos protocolos: Ejemplo: SOAP puede funcionar sobre HTTP, SMTP, JMS,… Los defensores de REST dicen que prácticamente siempre se usa HTTP también en RPC (SOAP casi siempre va sobre HTTP). Defensores de RPC dicen que HTTP a veces es inadecuado: Es poco eficiente. Hay cosas que no soporta: Ejemplo: HTTP no puede garantizar la entrega de un mensaje. Hay servicios web reales que utilizan SOAP sobre JMS, porque JMS sí proporciona esa funcionalidad. REST en la Práctica (1) La mayor parte de servicios que se autodenominan REST se parecen más a lo que hemos visto en el Tema 3.3 que al enfoque RESTful. Normalmente sólo se utilizan GET (lectura) y POST (escritura) Prácticamente siempre hay recursos que realmente implementan operaciones no estándar. Pocas veces se asignan URLs a recursos que no representen realmente operaciones sino objetos. REST en la Práctica (y 2) Incluso en los servicios autodenominados RESTful es raro ver algunas características (hypermedia, servicios autodescriptivos). A pesar de esto, algunas de las ventajas más importantes se mantienen: Caches Accesibilidad sigue siendo mejor. Aprovechamiento de los mecanismos de HTTP. El estilo REST no supone una decisión de “todo o nada”: El que nuestro servicio tenga sólo algunas de las características de REST puede ser útil todavía. Conclusiones REST es un nuevo estilo arquitectónico para construir aplicaciones distribuidas basadas en los principios que hicieron exitosa a la web. REST modela un servicio como la aplciación de una serie de operaciones fijas sobre un conjunto de recursos referenciables universalmente. REST tiene el potencial para permitir la construcción de aplicaciones distribuidas mucho más escalables e interoperables, compuestas por cientos de servicios muy desacoplados entre sí. RPC es más flexible (REST “obliga” a modelar la aplicación de una forma concreta) y más amigable para el programador (independiente de XML y HTTP). La mayoría de aplicaciones distribuidas corporativas y B2B siguen modelándose según la arquitectura RPC, aunque REST tiene su importancia y gana terreno. A menudo, REST se usa para referirse a servicios web que usan exclusivamente HTTP / XML (en lugar de SOAP), incluso si la arquitectura de dichos servicios es básicamente RPC. Estos servicios se benefician de parte de las ventajas de REST.