Servicios web RESTful con HTTP. Parte II: ejemplos

En esta segunda parte vamos a analizar 2 ejemplos que nos servirán como base práctica a la hora de implementar nuestro propio servicio web RESTful. Vamos a dedicar esta entrada a analizar 2 APIs diferentes, una excelente y otra desastrosa: GitHub y CECA (Confederación Española de Cajas de Ahorros).

GitHub

ghjj

En este primer caso de uso, vamos a ver una API 100% RESTful [1] cuyo diseño es fantástico y hace uso de varias técnicas avanzadas que comentamos a continuación:

Versioning

Uno de los problemas que surgen cuando sacamos a producción una API es el de gestionar adecuadamente las versiones de la misma. Por la propia naturaleza de una API, debemos asumir que en cuanto ha salido a producción al menos 1 usuario va a hacer uso de ella de manera continuada, a pesar de que en el futuro saquemos versiones nuevas de la API que sustituyen a la versión actual. En estos casos, debemos de disponer de algún mecanismo que permita a los usuarios acceder a una versión u otra. Aparte de las soluciones obvias como cambiar la URL base, cambiar las URLs de los recursos, etc. GitHub resuelve este problema de manera muy elegante usando los Media-Types. De esta manera, un usuario que requiere estabilidad es las respuestas de la API, puede indicarlo así a través de la cabecera «Accept» en sus peticiones. GitHub define además una terminología que sigue las buenas prácticas del protocolo HTTP:

application/vnd.github[.version].param[+json]

Como vemos, podemos definir con qué versión estamos trabajando, y además el formato en que queremos la respuesta. Por ejemplo, si quisiéramos trabajar en todo momento con la v3 de la API en formato JSON, añadiríamos en nuestras peticiones la siguiente cabecera:

Accept: application/vnd.github.v3+json

De este modo, podemos asegurar que nuestra API usa la versión correcta y que a su vez procesa las respuestas en el formato adecuado de manera consistente.

Autenticación

OAuth2

GitHub posee varias maneras de autentificar y autorizar las peticiones que hacemos a la API. Esto es vital para mantener un mínimo de seguridad sobre las operaciones que realizamos. Cuando se trata de autentificación, un método que está de moda últimamente, y que se ha probado robusto y estable, es la autentificación OAuth, en especial la versión 2. Además, OAuth2 sirve nó sólo para autentificar APIs, sino para cualquier otro tipo de aplicación. Así por ejemplo no es raro ver hoy en día que este protocolo se usa en sitios tan populares como Google, Twitter, Facebook, etc. Eso ya de por sí es una prueba irrefutable de su viabilidad y conveniencia para aplicaciones como la nuestra.

Existen otros métodos, como la autentificación básica, pero está demostrado [2] que estos métodos no son del todo convenientes y actualmente se recomienda implementar otros métodos más seguros, como el que hemos comentado.

Paginación de resultados

Normalmente cuando mostramos datos a los usuarios en nuestros sitios web, utilizamos algún tipo de paginación, ya que no resulta muy práctico mostrar miles de registros de una vez, aparte de ser ineficiente en cuanto a recursos del servidor. El mismo principio se aplica también en el diseño de APIs, en dónde el resultado de una petición sobre un recurso que cuenta con miles de entradas debe estar paginado. Esto además implica que tenemos que indicar de alguna manera cómo vamos a paginar los datos y enlazar las páginas entre sí. GitHub resuelve esto de manera elegante primero, añadiendo 2 parámetros extra «page» y «per_page», que son autodescriptivos, y además haciendo uso de las cabeceras Link de HTTP [3], y que nos sirve para que nuestra aplicación pueda navegar automáticamente entre los resultados.

Otras técnicas

Hay muchas más técnicas que se nos quedan en el tintero, como HATEOAS, Rate Limiting, peticiones condicionales, etc. GitHub implementa de manera magistral estas técnicas, por lo que recomiendo encarecidamente estudiar a fondo este ejemplo antes de ponernos a diseñar nuestra propia API.

CECA

logo_ceca

Este último ejemplo es el paradigma de lo que jamás deberíamos hacer. La API de CECA, a pesar de ser un sistema de pago online (con todo lo que ello conlleva), es una de las peores APIs que he tenido que programar en mi vida. Está diseñada de manera tan errónea que, irónicamente, presenta un caso de estudio bastante interesante, especialmente en cómo podemos lidiar ante un problema de este estilo para que afecte lo menos posible a nuestros programas integrados.

Inconsistencia de parámetros

Para poder hacer pagos con esta API necesitamos enviar una serie de parámetros que se nos detallan en la documentación. El problema de este punto es que la inconsistencia es grande y uno no sabe muy bien en qué formato, qué tipos de parámetros ha de enviar y con qué restricciones. Uno no puede más que intentar adivinar las intenciones del diseñador e intentar adaptar a su programa dichos parámetros y tipos. Por no hablar de ciertos parámetros no configurables que aun así debemos enviar en las peticiones. ¿Qué sentido tiene añadir parámetros que no podemos modificar?

Especialmente sangrante es el parámetro «Firmar», en el que se nos obliga a calcular de una manera muy poco ortodoxa una firma para asegurar la autenticidad de los mensajes enviados. En vez de usar un sistema de autentificación estandarizado, nos obligan a implementar uno propio de dudosa efectividad, difícil de testear. Otro ejemplo más de que siempre es mejor escoger tecnologías y métodos ya estandarizados y dejar de reinventar la rueda. En nuestros programas podremos salvar fácilmente problemas con parámetros y no-estándares simplemente implementando los mismos como tipos/estándares nuevos dentro de las abstracciones de nuestro sistema, de manera que se presente transparente al resto del sistema. En programación orientada a objetos esto se resuelve de manera inmediata.

Terminología imprecisa y no estandarizada

Otro problema que se presenta a lo largo de toda la documentación es un uso impreciso e inadecuado de la terminología. Así por ejemplo se usa el término «formulario», cuando lo mismo no tiene nada que ver con una API, se usan códigos de países o idiomas no estándares, y otros ejemplos. Para resolver esto dentro de nuestro programa, lo recomendado es, como en el caso anterior, abstraer lo máximo posible todas estas invenciones en estándares usados y reconocidos. Por esta regla de tres, los códigos de los páises usarían el estándar ISO 3661 y el de las divisas el ISO 4217. Además, la mayoría de lenguajes de programación cuentan con librerías que implementan estos estandares y que resulta trivial integrar en nuestros programas.

Códigos de respuesta y contenidos

Como ya sabemos, utilizar códigos de respuesta adecuados y contenidos fácil de procesar es esencial en toda API que se las precie. Sin embargo, la API que nos ocupa ignora completamente esta obviedad; así por ejemplo no es raro ver que todas las respuestas utilizan el mismo código (200), sean correctas o incorrectas, y que el contenido de las mismas son documentos en HTML y cosas por el estilo. Esto causa que identificar correctamente lo que nos quiere comunicar la API sea una tarea complicada. Mi recomendación para atajar problemas de este estilo es aislar en la medida de lo posible este tipo de inconsistencias: para ello podemos echar mano de expresiones regulares (para leer contenidos en el documento e inferir una respuesta de ahí), o crawlers para intentar hallar contenido legible dentro de documentos HTML. Aislando esta parte de la API y adaptándola correctamente en códigos de respuesta y estructuras de dato fácilmente legibles, podremos ahorrar el engorro de programar casos especiales en nuestro consumidor de API genérico.

Conclusiones

Como hemos visto, en esto del diseño de APIs REST ya está prácticamente todo inventado. No hay más que echar mano de APIs bien diseñadas para ver que efectivamente los problemas más comunes están resueltos y de manera bastante elegante. Es recomendable apoyarse sobre esta base bien asentada a la hora de diseñar nuestras propias APIs en lugar de inventar códigos y sistemas no estandarizados y que lo único que hacen es añadir confusión y dificultad a nuestras APIs. En la tercera parte de esta serie de artículos veremos un caso de uso y diseñaremos una API desde 0 usando el framework para PHP Symfony y varias librerías extra.

[1] http://developer.github.com/v3/
[2] http://apiux.com/2013/07/10/oauth-2-trumps-basic-authentication/
[3] http://tools.ietf.org/html/rfc5988

WordPress › Error

Ha habido un error crítico en esta web.

Aprende más sobre el diagnóstico de WordPress.