Inek

Primeros pasos con Drogon

¡Tu mensaje fue recibido! Una vez que sea aprobado, estará visible para los demás visitantes.

Cerrar

15 Apr 2022

¿Alguna vez quisiste desarrollar una api rest de alto rendimiento y bajo consumo de memoria y cpu? En este post exploramos Drogon, un framework para desarrollo de aplicaciones HTTP en C++.

Configurando el proyecto

Lo primero que haremos será ir a la documentación del proyecto para consultar las instrucciones de instalación.

Si hemos instalado el framework debidamente, tras ejecutar en nuestra consola el comando drogon_ctl -v o dg_ctl -v, deberíamos ver una salida como esta:

$ drogon_ctl -v
     _                             
  __| |_ __ ___   __ _  ___  _ __  
 / _` | '__/ _ \ / _` |/ _ \| '_ \ 
| (_| | | | (_) | (_| | (_) | | | |
 \__,_|_|  \___/ \__, |\___/|_| |_|
                 |___/             

A utility for drogon
Version: 1.7.5
Git commit: 6971f84dae707da7d37e1e045d5e06c6b3443e5d
Compilation: 
  Compiler: /usr/bin/c++
  Compiler ID: GNU
  Compilation flags: -std=c++17 -I/usr/include/jsoncpp -I/usr/local/include
Libraries: 
  postgresql: no  (batch mode: no)
  mariadb: no
  sqlite3: yes
  openssl: yes
  brotli: yes
  boost: no
  hiredis: no
  c-ares: yes

Para conocer los componentes elementales del framework, vamos a implementar una aplicación que nos permita realizar facturas desde nuestro navegador. Comencemos ejecutando el comando dg_ctl create project invoicer:

$ dg_ctl create project invoicer
create a project named invoicer

El comando create project creará una carpeta con el nombre que hayamos informado como último argumento. Esta es estructura de la carpeta de nuestro proyecto:

.
├── build
├── CMakeLists.txt
├── config.json
├── controllers
├── filters
├── main.cc
├── models
│   └── model.json
├── plugins
├── test
│   ├── CMakeLists.txt
│   └── test_main.cc
└── views

En el archivo main.cc verás que se indica cuál es el puerto que escuchará nuestro server. Por defecto es el 80. En mi caso, este puerto lo tengo ocupado con otra aplicación por lo que, si ejecuto mi aplicación para escuchar el mismo puerto, me saltará el siguiente error:

20220415 20:08:53.228508 UTC 8336 FATAL Permission denied (errno=13) , Bind address failed at 0.0.0.0:80 - Socket.cc:67

A fines de evitar este error, vamos a cambiar el puerto que escuchará nuestro servicio. Esto lo podemos hacer o bien editando el archivo config.json o bien configurando el listener:

drogon::app().addListener("0.0.0.0",8081);

Para compilar este proyecto, debemos ir a la carpeta build y ejecutar los comandos cmake .. y make para compilar nuestro código y generar el ejecutable:

$ cd build
$ cmake ..
$ make

Cada vez que queramos compilar nuestro código y generar un nuevo ejecutable, deberemos ejecutar cmake y make estando en la carpeta build.

Desde la propia carpeta build, podemos ejecutar nuestra aplicación con el comando ./invoicer para luego cargar en nuestro navegador la url http://localhost:8081. Esto es lo que deberíamos ver:

Drogon Not found

Este 404 es el error esperable ya que no hemos creado vistas ni handlers. Si ponemos un index.html en nuestra carpeta build y refrescamos nuestro navegador, en lugar del 404 inicial, veremos el contenido de nuestro index.html.

Añadir la vista inicial

El próximo paso será configurar los html de nuestra aplicación.

Vamos a crear una carpeta llamada docroot en la raíz de nuestro proyecto y ponemos un index.html como este:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Invoicer!</title>
</head>
<body>
  This is our invoicer
</body>
</html>

Modificamos el archivo CMakeLists.txt para indicarle que copie nuestro docroot a nuestro directorio build:

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                   COMMAND ${CMAKE_COMMAND} -E copy_directory
                       ${CMAKE_SOURCE_DIR}/docroot/
                       ${CMAKE_CURRENT_BINARY_DIR}/docroot)

Vamos a indicar a nuestra aplicación la ruta del directorio root:

    drogon::app()
        .addListener("0.0.0.0",8081)
        .setDocumentRoot("docroot");

Compilamos, ejecutamos nuestra aplicación y cargamos en nuestro browser localhost:8081. Si todo ha ido bien, deberíamos ver nuestro index.

Nuestro primer controller

El siguiente paso será añadir nuestro primer controller. Este es el comando que Drogon nos proporciona para crear controllers que heredan de HttpSimpleController. Debemos ejecutarlo estando dentro de la carpeta controllers:

dg_ctl create controller v1::invoices::list

El resultado de ejecutar el comando anterior serán los archivos v1_invoices_list.h y v1_invoices_list.cc.

Cuando abrimos v1_invoices_list.h, vemos una definición de una clase con un método público y las siguientes macros:

PATH_LIST_BEGIN
// list path definitions here;
// PATH_ADD("/path", "filter1", "filter2", HttpMethod1, HttpMethod2...);
PATH_LIST_END

Drogon nos simplifica la declaración de rutas y métodos con PATH_ADD. Esta macro insertará el código necesario para que nuestro controller sea capaz de procesar el path y método que le indiquemos. Vamos a añadir nuestro primer endpoint reescribiendo las líneas citadas de esta manera:

PATH_LIST_BEGIN
PATH_ADD("/v1/invoices", Get);
PATH_LIST_END

En el archivo v1_invoices_list.cc vamos a poner la lógica para este endpoint dentro de la función asyncHandleHttpRequest:

Json::Value invoice1(Json::objectValue);
invoice1["id"] = "1";
invoice1["customer"] = "Antonio D. Bain";
invoice1["total"] = 10.50;

Json::Value invoice2(Json::objectValue);
invoice2["id"] = "2";
invoice2["customer"] = "Grace R. Shaw";
invoice2["total"] = 34.54;

Json::Value invoices(Json::arrayValue);
invoices.append(invoice1);
invoices.append(invoice2);

auto resp=HttpResponse::newHttpJsonResponse(invoices);
resp->setStatusCode(k200OK);
resp->setContentTypeCode(CT_APPLICATION_JSON);
callback(resp);

Ahora, tras compilar y ejecutar nuestra aplicación, el comando curl http://localhost:8081/v1/invoices debería retornarnos un json.

Para terminar, he añadido código al index.html para hacer la llamada a nuestro nuevo endpoint y escribir los datos en una tabla. Puedes ver los cambios en el repositorio que he preparado para este proyecto.

Conclusión

En este post hemos instalado el framework Drogon. Luego hemos configurado el framework para que nuestra aplicación pueda cargar archivos html y añadimos un endpoint para una api rest.

En entregas futuras añadiremos la interacción con una base de datos para empezar a dar soporte a otras funcionalidades.

¿Qué te pareció el post?

No hay comentarios.