What is API gateway stage?

Consumiendo APIs en C: Guía práctica con libcurl

hace 4 años

★★★★★Valoración: 4.26 (6002 votos)

En la actualidad, el consumo de APIs web se ha convertido en una práctica fundamental para el intercambio de datos entre aplicaciones. Si bien existen numerosos tutoriales y ejemplos en lenguajes como JavaScript, Python o PHP, el lenguaje C, a menudo asociado con la programación a nivel de sistema, rara vez se considera para esta tarea. No obstante, C es perfectamente capaz de gestionar peticiones API, lo que lo convierte en una opción viable para escenarios como sistemas de punto de venta (PoS), dispositivos IoT o aplicaciones embebidas, donde C ya se utiliza por su eficiencia y control de bajo nivel.

How to work with API in C?
A SIMPLE C PROGRAM TO FETCH DATA FROM AN API INVOLVES THE FOLLOWING COMPONENTS: , Initializing libcurl. , Configuring the API request (URL, HTTP method, headers, etc.). , Receiving and storing the response. , Cleaning up resources.

Este artículo explora cómo consumir APIs en C, aprovechando la librería libcurl. Al finalizar la lectura, comprenderás cómo obtener y procesar datos de APIs utilizando C, y por qué este enfoque sigue siendo relevante incluso en el desarrollo moderno.

Índice de Contenido

¿Por qué utilizar C para consumir APIs?

Aunque los lenguajes de alto nivel dominan el desarrollo web, C sigue siendo una opción práctica para consumir APIs en casos de uso específicos:

  • Rendimiento: C ofrece un alto rendimiento y una sobrecarga mínima, lo que lo hace ideal para entornos con recursos limitados, como dispositivos IoT. En aplicaciones donde la velocidad y la eficiencia son críticas, C puede superar a lenguajes interpretados o con máquinas virtuales.
  • Control: La gestión directa de la memoria permite una optimización precisa, especialmente importante para sistemas embebidos. En estos sistemas, cada byte de memoria y cada ciclo de reloj cuentan, y C permite un control absoluto sobre estos recursos.
  • Interoperabilidad: El uso extendido de C significa que se integra bien con operaciones a nivel de sistema, como el control de hardware, sensores u otros periféricos. Si tu aplicación necesita interactuar directamente con el hardware y al mismo tiempo consumir datos de APIs, C proporciona una solución cohesiva.
  • Longevidad: Las aplicaciones construidas en C a menudo tienen largos ciclos de vida, especialmente en industrias como el comercio minorista o la manufactura. La estabilidad y la compatibilidad a largo plazo de C son ventajas significativas para proyectos con requisitos de mantenimiento prolongado.

Introducción a libcurl: la herramienta para HTTP en C

Para consumir APIs en C, libcurl es la librería de referencia. Es una librería de código abierto, portable y rica en funcionalidades para gestionar peticiones de red a través de HTTP, HTTPS, FTP y más. Soporta:

  • Realizar peticiones GET, POST y otros métodos HTTP.
  • Gestión de cabeceras y autenticación.
  • Procesamiento eficiente de respuestas.

libcurl simplifica enormemente la tarea de realizar solicitudes HTTP en C, abstrayendo la complejidad de la gestión de sockets y protocolos de red. Su amplia documentación y comunidad activa la convierten en una opción sólida para cualquier proyecto que necesite comunicación HTTP en C.

Pasos básicos para consumir una API en C

Vamos a recorrer el proceso de consumir una API utilizando C, centrándonos en un ejemplo práctico de obtención de datos JSON.

Configuración e instalación

Para utilizar libcurl, necesitas instalarla en tu sistema. Para la mayoría de las distribuciones de Linux, esto se puede hacer con:

sudo apt-get install libcurl4-openssl-dev

En Windows, puedes descargar binarios precompilados desde el sitio web de libcurl: https://curl.se/download.html

En macOS, si utilizas Homebrew, puedes instalarla a través de:

brew install curl

Asegúrate de tener instaladas las cabeceras de desarrollo (dev) para poder compilar programas en C que utilicen libcurl.

Estructura de tu programa en C

Un programa sencillo en C para obtener datos de una API implica los siguientes componentes:

  1. Inicializar libcurl.
  2. Configurar la petición API (URL, método HTTP, cabeceras, etc.).
  3. Recibir y almacenar la respuesta.
  4. Liberar recursos.

Aquí tienes un programa de ejemplo para obtener datos JSON de una API pública:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> // Struct para guardar los datos de la respuesta struct Memory { char *response; size_t size; }; // Función callback para manejar los datos recibidos de la API static size_t ResponseCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t totalSize = size * nmemb; struct Memory *mem = (struct Memory *)userp; printf(". %zu %zu\n", size, nmemb); char *ptr = realloc(mem->response, mem->size + totalSize + 1); if (ptr == NULL) { printf("Not enough memory to allocate buffer.\n"); return 0; } mem->response = ptr; memcpy(&(mem->response[mem->size]), contents, totalSize); mem->size += totalSize; mem->response[mem->size] = '\0'; return totalSize; } int main() { CURL *curl; CURLcode res; struct Memory chunk; chunk.response = malloc(1); // Inicializar chunk de memoria chunk.size = 0; // Sin datos aún curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(curl) { // Establecer la URL del endpoint de la API char access_token[] = "your-access-token"; char slug[] = "home"; char version[] = "draft"; char url[256]; snprintf(url, sizeof(url), "https://api.storyblok.com/v2/cdn/stories/%s?version=%s&token=%s", slug, version, access_token); // Imprimir la URL printf("URL: %s\n", url); // inicializando libcurl // estableciendo la URL curl_easy_setopt(curl, CURLOPT_URL, url); // Seguir redirecciones curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Establecer la función callback para manejar los datos de respuesta curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ResponseCallback); // Pasar la estructura Memory a la función callback curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); // Realizar la petición HTTP GET res = curl_easy_perform(curl); // Comprobar si hay errores if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { printf("Response data size: %zu\n", chunk.size); //printf("Response data:\n%s\n", chunk.response); } // Limpieza curl_easy_cleanup(curl); } // Liberar memoria asignada free(chunk.response); curl_global_cleanup(); return 0; } 

Pasos para ejecutar

  1. Guarda el código en un archivo, por ejemplo, get.c.
  2. Compílalo con el siguiente comando:
gcc get.c -o get -lcurl
  1. Ejecuta el programa compilado:
./get

Comprendiendo el mecanismo de callback en respuestas HTTP con libcurl

Al trabajar con libcurl para manejar respuestas HTTP en C, es importante entender el comportamiento de la función callback. La función callback que defines para procesar los datos de respuesta, como la función ResponseCallback, puede ser invocada múltiples veces para una sola respuesta HTTP. Aquí te explicamos por qué y cómo funciona esto.

¿Por qué se invoca el callback múltiples veces?

El mecanismo de callback en libcurl está diseñado para manejar datos de forma eficiente y flexible. En lugar de esperar a que se descargue la respuesta completa antes de procesarla, libcurl procesa la respuesta en pequeños fragmentos, llamando a tu función callback a medida que se recibe cada fragmento.

Este comportamiento permite:

  • Uso eficiente de memoria: al procesar fragmentos de forma incremental, evitas la necesidad de asignar un gran bloque de memoria por adelantado para toda la respuesta. Esto es crucial cuando se manejan respuestas grandes o en sistemas con memoria limitada.
  • Procesamiento en streaming: puedes procesar o actuar sobre cada fragmento a medida que llega, lo cual es útil para transmitir respuestas grandes o manejar datos en tiempo real. Por ejemplo, podrías procesar un stream de video o audio a medida que se descarga.

¿Cómo funciona?

Cada vez que se recibe un fragmento de datos del servidor, libcurl llama a tu función callback. El tamaño de cada fragmento depende de las condiciones de la red, los tamaños de los buffers y la lógica interna de libcurl.

El callback tiene que acumular los fragmentos, reconstruyendo finalmente la respuesta completa.

Aquí tienes una secuencia de ejemplo:

  1. El servidor comienza a enviar la respuesta.
  2. libcurl recibe el primer fragmento y llama al callback.
  3. El callback procesa o almacena el fragmento.
  4. libcurl recibe el siguiente fragmento y llama al callback de nuevo.
  5. Este proceso continúa hasta que se recibe la respuesta completa.

Explicación paso a paso del código fuente de la función ResponseCallback

La función ResponseCallback es la función que se llama cuando libcurl recibe datos.

Declaración de la función

static size_t ResponseCallback(void *contents, size_t size, size_t nmemb, void *userp)
  • void *contents: este es un puntero a los datos recibidos del servidor. libcurl proporciona este buffer y lo llena con los datos que descarga.
  • size_t size y size_t nmemb: representan el tamaño de cada bloque de memoria (size) y el número de bloques (nmemb). Juntos, size * nmemb da el tamaño total de los datos recibidos en este fragmento.
  • void *userp: este es un puntero definido por el usuario que se pasa a la función callback a través de curl_easy_setopt(curl, CURLOPT_WRITEDATA, ...). En este ejemplo, es un puntero a un objeto struct Memory, que almacena la respuesta completa.

Calcular el tamaño total de los datos

size_t totalSize = size * nmemb;

Esto calcula el tamaño total del fragmento de datos actual recibido multiplicando el tamaño de un bloque (size) por el número de bloques (nmemb).

Por ejemplo, si el servidor envía 8 bloques de 256 bytes cada uno, totalSize será 8 * 256 = 2048 bytes.

Acceder a los datos del usuario (struct Memory)

struct Memory *mem = (struct Memory *)userp;

El puntero userp se convierte a un struct Memory *. Esta estructura se pasó anteriormente en el programa principal y se utiliza para acumular los datos recibidos.

La estructura Memory se define como:

struct Memory { char *response; size_t size; };
  • response: una cadena asignada dinámicamente que almacena los datos descargados.
  • size: el tamaño actual de la cadena response.

Reasignar memoria

char *ptr = realloc(mem->response, mem->size + totalSize + 1);

Redimensiona el buffer de respuesta para acomodar el nuevo fragmento de datos:

  • mem->size: el tamaño actual del buffer.
  • totalSize: el tamaño del nuevo fragmento.
  • +1: espacio para el terminador nulo (\0) para que sea una cadena C válida.
  • realloc: reasigna memoria dinámicamente para el buffer de respuesta.

Si la asignación falla, realloc devuelve NULL y la memoria antigua sigue siendo válida.

Manejar errores de asignación de memoria

if (ptr == NULL) { printf("Not enough memory to allocate buffer.\n"); return 0; }

Si la asignación de memoria falla (entonces ptr es NULL), imprime un mensaje de error y devuelve 0. Devolver 0 indica a libcurl que aborte la transferencia.

Actualizar el buffer

mem->response = ptr; memcpy(&(mem->response[mem->size]), contents, totalSize);
  • mem->response = ptr: asigna la memoria recién asignada de nuevo al puntero response.
  • memcpy: copia el nuevo fragmento de datos desde contents al buffer:
    • &(mem->response[mem->size]): la ubicación en el buffer donde se deben añadir los nuevos datos (final de los datos actuales).
    • contents: los datos recibidos del servidor.
    • totalSize: el tamaño de los datos a copiar.

Actualizar el tamaño total

mem->size += totalSize;

Incrementa el tamaño del buffer de respuesta para reflejar el nuevo tamaño total después de añadir el nuevo fragmento.

Terminar la cadena de respuesta con nulo

mem->response[mem->size] = '\0';

Añade un terminador nulo al final del buffer de respuesta para que sea una cadena C válida.

Esto asegura que la respuesta pueda ser tratada de forma segura como una cadena regular terminada en nulo.

Devolver el tamaño total

return totalSize;

Devuelve el número de bytes procesados (totalSize).

Esto indica a libcurl que el fragmento de datos se ha manejado correctamente.

¿Cuándo elegir C para APIs?

Utiliza C para consumir APIs cuando:

  • El rendimiento es crucial: C es ideal para aplicaciones donde la velocidad es un factor crítico. Si estás construyendo un sistema de trading de alta frecuencia, un motor de juegos o cualquier aplicación que requiera la máxima velocidad de procesamiento, C es una excelente elección.
  • Integración a nivel de sistema: necesitas combinar peticiones de red con operaciones de hardware (por ejemplo, obtener datos para un sistema PoS que controla periféricos). En escenarios donde la aplicación necesita interactuar directamente con el sistema operativo, el hardware o bibliotecas de bajo nivel, C ofrece una integración natural y eficiente.
  • Sistemas embebidos: los dispositivos con recursos limitados se benefician de la eficiencia de C. En el mundo de los sistemas embebidos, donde la memoria y la potencia de procesamiento son a menudo escasas, la eficiencia de C es fundamental. Desde microcontroladores hasta sistemas operativos en tiempo real, C es el lenguaje predominante.
  • Curiosidad y exploración: A veces, utilizas C simplemente porque disfrutas programando y quieres desafiarte explorando un lenguaje de bajo nivel para tareas a menudo reservadas para lenguajes de nivel superior. ¡Es una excelente manera de profundizar en tu comprensión de cómo funcionan las cosas por debajo del capó! Aprender a consumir APIs en C puede proporcionarte una perspectiva más profunda sobre la comunicación de red y la gestión de recursos.

Conclusión

Consumir APIs en C puede parecer poco convencional en el mundo actual de la programación de alto nivel, pero es una herramienta poderosa para escenarios que requieren rendimiento, control e integración con operaciones a nivel de sistema. Utilizando librerías como libcurl, los desarrolladores pueden integrar fácilmente peticiones HTTP en aplicaciones C, cerrando la brecha entre las APIs modernas y la programación tradicional a nivel de sistema.

Con este conocimiento, puedes construir aplicaciones C que interactúen de forma fluida con APIs, demostrando que C sigue siendo relevante incluso en flujos de trabajo de desarrollo modernos. La combinación de la eficiencia de C con la capacidad de consumir APIs web abre un abanico de posibilidades para aplicaciones robustas y de alto rendimiento en diversos dominios.

Subir