Revista Tecnología

Cómo crear una API RESTful en C++ y usarlo como queramos. [ Con código fuente y ejemplo disponible ]

Publicado el 10 octubre 2016 por Gaspar Fernández Moreno @gaspar_fm
  • Estás aquí :
  • Poesía Binaria
  • Cómo crear una API RESTful en C++ y usarlo como queramos. [ Con código fuente y ejemplo disponible ]

Cómo crear RESTful usarlo como queramos. código fuente ejemplo disponibleCómo crear una API RESTful en C++ y usarlo como queramos. [ Con código fuente y ejemplo disponible ]

Las aplicaciones en Internet crecen a un ritmo frenético. Y para que éstas crezcan es necesario que unos sistemas se comuniquen con otros. Dentro de la misma aplicación, existe una comunicación con una base de datos, con un sistema de cachés con un servidor de correo, etc. Incluso dentro de una misma plataforma, existe una comunicación entre la capa de datos y la capa de interfaz de usuario (podemos verlo en aplicaciones móviles que acceden a datos alojados en un servidor web).

La forma de comunicarse (canal de comunicación, elementos a los que podemos acceder, quién puede acceder, etc), es a lo que llamamos API (o Application Programming Interface, Interfaz de Programación de Aplicaciones). Históricamente, estas APIs hacían referencia al diálogo entre programas o entre programa y biblioteca. Y lo encontramos continuamente a muchos niveles. Por ejemplo en los métodos que proporciona nuestro sistema operativo para leer y escribir un archivo: cuando programamos una aplicación normalmente no accedemos directamente a disco, hablamos con el sistema operativo para que nos simplifique la tarea; cuando accedemos a una webcam o scanner, tendremos un driver por encima que nos proporcionará un método sencillo para acceder a las imágenes; cuando se dibuja una ventana en nuestra pantalla, para que todas mantengan el mismo look&feel, accederemos a nuestro entorno de ventanas y le pediremos que dibuje una ventana; o cuando introducimos un evento de calendario desde nuestro teléfono móvil y éste aparezca al mismo tiempo en nuestra tablet y nuestro ordenador, las aplicaciones de cada dispositivo se comunicarán con un servidor para hacerlo.

Para comunicar nuestras aplicaciones por Internet hay muchas formas de hacerlo, es decir, las aplicaciones podrán utilizar muchos idiomas para dicha comunicación. La magia reside en que independientemente del lenguaje en el que estén programados, si se define bien una API para realizar esta comunicación, podremos realizar acciones en una aplicación web desde otra aplicación (escritorio, web, móvil, etc), o podemos, desde nuestra aplicación, acceder a Facebook, recursos de Google, Twitter o incluso bases de datos como la de Have I Been Pwned, bases de datos de geolocalización, letras de canciones, equivalencias, catálogos de tiendas online como Magento o Prestashop, etc.

Algo más sobre APIs Web

Para comunicarnos con otros servicios, podemos utilizar múltiples métodos, y uno de ellos es establecer una comunicación de red, y ya que para pedir algo por Internet, utilizamos TCP/IP. ¿ Por qué no usar lo mismo ? Bueno, y ya puestos, si para acceder a una web utilizamos el puerto 80, al que normalmente muchos firewalls están configurados para permitir y que, si nos pilla en alguna red extraña (facultades, redes de empresas, puntos de acceso públicos, etc) no vamos a tener problemas para conectar. Oye y, ¿ por qué no utilizamos el protocolo HTTP para comunicarnos con los servidores ? Haríamos peticiones como si fuera una página web, podríamos utilizar cookies de forma transparente, cabeceras, compresión y los navegadores lo entenderían, pues perfecto.
A partir de ahí encontramos muchas tecnologías que se han utilizado para establecer este tipo de comunicaciones. Por ejemplo SOAP (Simple Object Access Protocol), WSDL (Web Services Description Language) y últimamente REST (REpresentational state transfer) está muy de moda (aunque tiene ya su tiempo).

Una particularidad de las APIs REST, es que para varias operaciones se utiliza una URL de entrada similar, pero métodos HTTP diferentes según lo que queramos. En el ejemplo de más abajo, hemos descrito el recurso films que tendrá habilitados los métodos GET, POST, PUT, PATCH y DELETE, dependiendo de la operación que vayamos a realizar.

Bibliotecas utilizadas

Para el desarrollo de este proyecto he utilizado Glove, que es uno de mis proyectos, aunque podemos basarnos en él para utilizar otra biblioteca para interacción con la web. En la última actualización se pueden crear conexiones SSL de forma muy sencilla. Y JSON for Modern C++, aunque también lo podemos ver si clonamos el repositorio de Github de Glove.

El hecho de utilizar la biblioteca de json es para parsear las entradas de datos, las salidas son excesivamente simples y están puestas a mano, aunque bien podría haber utilizado esta gran biblioteca para procesarlas.

Nuestro ejemplo de API REST

El ejemplo que cuento a continuación (más abajo veremos el código fuente) trata de una base de datos de películas (En C++ lo haremos con un vector de un struct, pero lo podremos complicar aún más). Dicho vector lo hemos metido en una clase, y dicha clase (Cinema) tiene métodos para:

  • Listar los contenidos del vector (ver todas las películas)
  • Presentar datos de un elemento (ver una película)
  • Modificar todos los datos de un elemento (editar una película)
  • Parchear un elemento (editar un dato individual de una película)
  • Eliminar un elemento (eliminar una película)

Ya tenemos nuestro CRUD en C++. Al menos tenemos métodos sencillos para gestionar los elementos del vector, hacerlo directamente en las peticiones podrá ser más efectivo y más rápido, pero en este punto prefiero que el código se lea y se mantenga bien.

Luego se define un punto de entrada a nuestra api que será /films/ por lo que si hacemos:

  • GET /films/ : obtendremos un listado de películas de nuestra base de datos. En JSON.
  • GET /films/[id] : obtendremos los datos de la película con dicho ID. En JSON.
  • POST /films/ : se añade una película. Los datos entran en JSON y se devuelve un JSON con el estado y la URL donde podemos encontrar la película añadida
  • PUT /films/[id] : Modificamos la película con dicho ID cambiando todos sus elementos. Entrada en JSON y respuesta estado y URL como el método anterior.
  • PATCH /films/[id] : Editamos un elemento de la película con dicho ID. Entrada en JSON y respuesta similar al anterior.
  • DELETE /films/[id] : Eliminamos la película con el ID especificado. La respuesta será un status: ok en JSON.

Para hacer esto, crearemos una clase y un método para cada petición. En Glove, los métodos que atienden las peticiones llevarán dos argumentos, uno para la petición (request) y otro para la respuesta (response). Dicha clase utilizará por detrás un objeto de clase Cinema (la clase que hicimos antes).

La implementación

Aquí está el código fuente completo del ejemplo:

Para compilar podemos hacer:

g++ -o apiexample2 apiexample.cc glovehttpserver.cpp glove.cpp glovewebsockets.cpp glovecoding.cpp glovehttpcommon.cpp -lpthread -DENABLE_OPENSSL=0 -DENABLE_COMPRESSION=0 -std=c++11


En la última versión de Glove, separé el código en varios archivos más, para tener rutinas de codificación y rutinas comunes de HTTP separadas y no hacer tan terribles las compilaciones y tan grandes los archivos fuente... y un archivo para soporte WebSocket... que ya comentaré un día de estos en otro post.

Por otro lado, en el ejemplo anterior estamos compilando sin soporte para SSL (-DENABLE_OPENSSL) ni compresión (-DENABLE_COMPRESSION). Aunque está bien activarlos para experimentar con estas capacidades también (activar SSL nos permite utilizar rutinas de criptografía nativas de OpenSSL que funcionan mejor que los reemplazos.
Para compilarlo con esos soportes debemos tener las bibliotecas libssl y zlib que seguramente encontréis en vuestra distribución (con cabeceras de desarrollo) y ejecutar:

g++ -o apiexample2 apiexample.cc glovehttpserver.cpp glove.cpp glovewebsockets.cpp glovecoding.cpp glovehttpcommon.cpp -lpthread -lssl -lcrypto -lz -std=c++11


Incluso podemos compilar con-O4 para optimizar el ejecutable resultante tanto en tamaño como en rendimiento.

Comprimiendo la salida

Para comprimir la salida generada, basta con especificar los métodos de compresión activados (por ahora sólo están implementados gzip y deflate) y pasarlos cuando estés creando el servidor:

Dedicaré otro post a la creación de servidores con OpenSSL activado desde aquí, aunque debemos tener mucho cuidado cuando en la salida figure parte de la entrada (no es el caso ahora mismo), y tengamos SSL y compresión activadas, ya que podríamos ser vulnerables antes BREACH.

Ejemplo de peticiones

Las peticiones que realicemos, las podremos hacer desde un script en PHP, desde Javascript, tanto en servidor como en cliente, de esta última manera será el usuario el que con su navegador haga las peticiones, desde Java, C, C++, o incluso desde Bash con cURL. Y, con este último voy a hacer la demostración. Para las pruebas, he iniciado el servidor en el puerto 8080 (aunque si queremos, podemos redirigir ciertas rutas de Apache a nuestro servidor y acceder desde el puerto 80, con lo que puede coexistir con lo que actualmente estemos sirviendo.
Sin más dilación pondré aquí algunas peticiones y su respuesta:

GET /films/

curl http://localhost:8080/films/

"title": "Doctor Strange",

"director": "Scott Derrickson",

"stars": "Rachel McAdams, Benedict Cumberbatch, Mads Mikkelsen",

"title": "The Magnificent Seven",

"director": "Antoine Fuqua",

"stars": "Denzel Washington, Chris Pratt, Ethan Hawke",

"title": "Bridget Jones' Baby",

"director": "Sharon Maguire",

"stars": " Renée Zellweger, Gemma Jones, Jim Broadbent",

"director": "Oliver Stone",

"stars": " Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo",

"title": "Don't Breathe",

"director": "Fede Alvarez",

"stars": "Stephen Lang, Jane Levy, Dylan Minnette",

"title": "Suicide Squad",

"director": "David Ayer",

"stars": "Will Smith, Jared Leto, Margot Robbie",

GET /films/4

curl http://localhost:8080/films/4

"director": "Oliver Stone",

"stars": " Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo",

POST /films/

curl -XPOST http://localhost:8080/films/ -d '{ "title" : "The Great Wall", "director": "Yimou Zhang", "stars" : "Pedro Pascal, Matt Damon, Willem Dafoe", "duration": 120 }'

"target": "http://localhost:8080/films/7″ }

PUT /films/

curl -XPUT http://localhost:8080/films/5 -d '{ "title" : "Inferno", "director": "Ron Howard", "stars" : "Tom Hanks, Felicity Jones, Ben Foster", "duration": 121 }'

"target": "http://localhost:8080/films/5″ }

PATCH /films/

curl -XPATCH http://localhost:8080/films/7 -d '{ "duration" : 124, "stars": "Will Smith, Jared Leto, Margot Robbie, Viola Davis" }'

"target": "http://localhost:8080/films/7″ }

DELETE /films/

curl -XDELETE http://localhost:8080/films/2

GET /films/ - de nuevo

Y, para ver cómo ha quedado todo:

curl http://localhost:8080/films/

"title": "Doctor Strange",

"director": "Scott Derrickson",

"stars": "Rachel McAdams, Benedict Cumberbatch, Mads Mikkelsen",

"title": "Bridget Jones' Baby",

"director": "Sharon Maguire",

"stars": " Renée Zellweger, Gemma Jones, Jim Broadbent",

"director": "Oliver Stone",

"stars": " Joseph Gordon-Levitt, Shailene Woodley, Melissa Leo",

"director": "Ron Howard",

"stars": "Tom Hanks, Felicity Jones, Ben Foster",

"title": "Suicide Squad",

"director": "David Ayer",

"stars": "Will Smith, Jared Leto, Margot Robbie",

"title": "The Great Wall",

"director": "Yimou Zhang",

"stars": "Will Smith, Jared Leto, Margot Robbie, Viola Davis",

Lo que falta por implementar

Aunque con esta API podemos hacer muchas cosas, incluso algunas cosas de esta lista se pueden implementar sobre los callbacks de acciones, no estaría mal que, en el futuro fueran de forma nativa:

  • Utilizar códigos de salida adecuados, como "201 Created" en operaciones POST, o "204 No Content" en operaciones DELETE en lugar de devolver un JSON con status=ok.
  • Autentificación: lograr que el sistema sepa qué usuario está pidiendo qué cosa. Aquí tenemos varios métodos que podrán ser habilitados o deshabilitados. El primero es autentificación HTTP, la básica de toda la vida, luego se podrían crear métodos especiales de identificación de usuarios que se pueden hacer persistentes para todas las peticiones de un mismo usuario mediante cookies o cabeceras especiales. Como último paso se podría implementar OAuth2, y darse un permiso temporal a una aplicación/usuario.
  • Autorización de peticiones. Ahora mismo todo está permitido, por todo el mundo, aunque en el futuro debemos decidir qué usuarios tendrán capacidad para qué cosas. Esto puede ser peligroso ya que el momento en el que sabemos si algo se puede hacer o no puede variar. Es decir, a veces podremos determinar que un usuario no puede hacer una petición DELETE y por tanto podremos denegar el acceso antes de llegar al método de acción. Otras veces, tal vez un usuario puede hacer DELETE pero sólo sobre determinados objetos...
  • Invalidación de métodos (method override). En determinados sistemas, sólo GET y POST están permitidos (sobre todo si pasamos a través de proxys), por lo que se implementa la cabecera especial X-HTTP-Method-Override y sólo en peticiones POST, esto actúa como una petición nativa del tipo especificado:

    POST /films/54
    X-HTTP-Method-Override: DELETE

  • Limitación de tráfico. Devolver un 429 Too Many Requests cuando un usuario esté pidiendo demasiado
  • Cacheo. El resultado de peticiones GET podrá ser almacenado temporalmente en memoria o en algún motor de caché en lugar de calcularse. Esto será útil cuando las peticiones sean más complejas y la cantidad de datos mucho más grande. En este ejemplo no tiene mucho sentido.

Para leer más

Si te has quedado con ganas de más, te dejo algunas cosas:

Opiniones, colaboración

Si te animas, puedes dejar un comentario con tus ideas, sugerencias, comentarios, errores de compilación, etc. Y si te animas más y creas algún ejemplo más, ponte en contacto conmigo, y pongo tus ejemplos en la web. Y si te animas aún más y quieres colaborar con Glove, hazle un fork en GitHub y ¡¡empieza a picar!!

Foto principal: Berry Van Der Velden


Volver a la Portada de Logo Paperblog