sábado, 18 de octubre de 2025

Consumiendo una API pública con Kotlin & OkHttp

En el pasado post vimos cómo consumir un servicio Node desde una aplicación Java gracias a la librería OkHttp. Esta vez usaremos Kotlin como lenguaje base y Gradle para la administración de las dependencias y ejecución del proyecto.

¿Qué haremos?

1. Comenzaremos con la creación del directorio del proyecto:

$ mkdir proyecto-kotlin
$ cd proyecto-kotlin

El directorio del proyecto se verá así:

proyecto-kotlin/
   .gitattributes
   .gitignore
   .gradle
   .kotlin
   app/
   build/
   gradle/
   gradle.properties
   gradlew
   gradlew.bat
   settings.gradle.kts

2. Agregamos la dependencia de ``OkHttp`` en el archivo ``build.gradle.kts``:

plugins {
    alias(libs.plugins.kotlin.jvm)
    application
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0") // Moved to implementation
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation(libs.junit.jupiter.engine)
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation(libs.guava)
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.3")
    testImplementation("org.mockito:mockito-core:5.12.0")
    testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = "org.inforhomex.AppKt"
}

tasks.named<Test>("test") {
    useJUnitPlatform()
}

Con esto se podrá descargar la dependencia.

3. Editamos el programa ``app/src/main/kotlin/org/inforhomex/App.kt``.

package org.inforhomex

import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException

private const val URL_API = "https://api.chucknorris.io/jokes/random"

fun run(client: OkHttpClient = OkHttpClient()) {
    val request = Request.Builder().url(URL_API).build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("Unexpected code $response")
        for ((name: String, value: String) in response.headers) {
            println("$name: $value")
        }
        println(response.body?.string() ?: "No response body")
    }
}

fun main() {
    run()
}

4. Creamos una prueba unitaria ``AppTest.kt``

package org.inforhomex


import okhttp3.*
import okio.Buffer
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.jupiter.MockitoExtension
import java.io.IOException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

@ExtendWith(MockitoExtension::class)
class AppTest {

    @Mock
    private lateinit var client: OkHttpClient

    @Mock
    private lateinit var response: Response

    @Mock
    private lateinit var responseBody: ResponseBody

    @Mock
    private lateinit var call: Call

    private val urlApi = "https://api.chucknorris.io/jokes/random"

    @BeforeEach
    fun setUp() {
        
        Mockito.`when`(client.newCall(Mockito.any(Request::class.java))).thenReturn(call)
    }

    @Test
    fun `test run success response`() {
        
        val mockJson = """
            {
                "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
                "id": "abc123",
                "url": "",
                "value": "Chuck Norris can divide by zero."
            }
        """
        Mockito.`when`(call.execute()).thenReturn(response)
        Mockito.`when`(response.isSuccessful).thenReturn(true)
        Mockito.`when`(response.body).thenReturn(responseBody)
        Mockito.`when`(responseBody.string()).thenReturn(mockJson)
        Mockito.`when`(response.headers).thenReturn(Headers.Builder().add("Content-Type", "application/json").build())

        
        val output = captureOutput {
            run(client) 
        }

        
        assert(output.contains("Content-Type: application/json"))
        assert(output.contains("Chuck Norris can divide by zero."))
    }

    @Test
    fun `test run unsuccessful response throws IOException`() {
       
        Mockito.`when`(call.execute()).thenReturn(response)
        Mockito.`when`(response.isSuccessful).thenReturn(false)
        Mockito.`when`(response.code).thenReturn(404)

       
        val exception = assertFailsWith<IOException> {
            run(client) 
        }
        assertEquals("Unexpected code 404", exception.message)
    }

    
    private fun captureOutput(block: () -> Unit): String {
        val outputStream = java.io.ByteArrayOutputStream()
        val printStream = java.io.PrintStream(outputStream)
        val originalOut = System.out
        System.setOut(printStream)
        try {
            block()
        } finally {
            System.setOut(originalOut)
        }
        return outputStream.toString()
    }
}

5. Compilamos y ejecutamos la aplicación:

$ gradle build
$ gradle run

Si todo va bien, veremos esto en la consola:

date: Sun, 19 Oct 2025 03:25:17 GMT
content-type: application/json
nel: {"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}
report-to: {"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=e%2BRTonTfjrgED0WXfh1o2mxbl6%2BLhQQ4LIu%2F1NhF4MM%3D\u0026sid=812dcc77-0bd0-43b1-a5f1-b25750382959\u0026ts=1760844317"}],"max_age":3600}
reporting-endpoints: heroku-nel="https://nel.heroku.com/reports?s=e%2BRTonTfjrgED0WXfh1o2mxbl6%2BLhQQ4LIu%2F1NhF4MM%3D&sid=812dcc77-0bd0-43b1-a5f1-b25750382959&ts=1760844317"
server: cloudflare
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
via: 1.1 heroku-router
cf-cache-status: DYNAMIC
cf-ray: 990d2ad69c2f69de-DFW
alt-svc: h3=":443"; ma=86400
{"categories":[],"created_at":"2020-01-05 13:42:20.262289","icon_url":"https://api.chucknorris.io/img/avatar/chuck-norris.png","id":"zDUih4UaS82sDRjSX0hlLQ","updated_at":"2020-01-05 13:42:20.262289","url":"https://api.chucknorris.io/jokes/zDUih4UaS82sDRjSX0hlLQ","value":"Chuck Norris always wanted to surf but couldn't. Everytime his board touches the water, the sea parts."}

¡La aplicación ha funcionado correctamente!

Continuaremos con esta serie de ejemplos en próximas entregas.

Enlaces:

https://square.github.io/okhttp/
https://www.baeldung.com/guide-to-okhttp
https://ironpdf.com/es/java/blog/java-help/okhttp-java/


viernes, 17 de octubre de 2025

Creando una aplicación web sencilla con cpp-httplib

En esta ocasión veremos cómo crear una sencilla aplicación web con cpp-httplib.

cpp-httplib es una biblioteca de C++11 que simplifica la creación de servidores y clientes HTTP/HTTPS, caracterizada por ser de un solo archivo de encabezado para facilitar la configuración. Está diseñada para ser fácil de usar, pero también ofrece funcionalidades avanzadas como soporte SSL, compresión, cliente y registro.

Requisitos:

  • Tener git instalado. 
  • Tener el compilador g++ instalado. 
  • Tener msys64 instalado.

1. Descargar cpp-httplib desde Github:

$ git clone https://github.com/yhirose/cpp-httplib.git

Esto nos clonará el repo y generará un directorio llamado ``cpp-httplib``

2. Creamos un proyecto C++ llamado ``holamundo`` y nos ubicamos en el:

$ mkdir holamundo
$ cd holamundo

3. Copiamos el archivo ``httplib.h`` del directorio ``cpp-httplib`` al directorio ``holamundo``:

4. Creamos el programa ``main.cpp`` de tal manera que la estructura del proyecto sería la siguiente:

holamundo/
  httplib.h
  main.cpp

5. Editamos el programa ``main.cpp`` para que tenga el siguiente contenido:

#include "httplib.h"

int main() {
    httplib::Server svr;

    svr.Get("/hola", [](const httplib::Request&, httplib::Response& res) {
        res.set_content("¡Hola, mundo!", "text/plain");
    });

    svr.listen("localhost", 8082);
    return 0;
}

6. Compilamos y creamos el ejecutable:

$ C:\msys64\ucrt64\bin\g++.exe main.cpp -o hola_mundo.exe -I. -pthread -lws2_32 -static-libgcc -static-libstdc++

7. Lanzamos el ejecutable:

$ hola_mundo.exe 

Si todo va bien, abrimos el navegador en la siguiente URL: http://localhost:8082/hola

Mostrará el mensaje de "¡Hola, mundo!"

Conclusiones: El desarrollo web en C++ no es habitual a nivel laboral. Es más frecuente encontrar desarrollos hechos en C#, Java, Python o hasta Ruby. Sin embargo, el lenguaje C++ no es famoso por ser el ideal para ello.

Con respecto a Crow, cpp-httplib es mucho más fácil de usar (solo requiere un encabezado y no tener que instalar tantas dependencias). Si quieres desarrollar aplicaciones web con C++ es mejor opción.

Enlaces:

https://github.com/yhirose/cpp-httplib
https://codemonkeyjunior.blogspot.com/2025/10/crow-un-framework-para-c.html

lunes, 13 de octubre de 2025

Crow: un framework para C++

Crow es un microframework de C++ para crear servicios web HTTP o Websocket. Utiliza enrutamiento similar al de Flask de Python. Se considera sumamente rápido y eficiente.

Veamos un ejemplo

holamundo.cpp

#include "crow.h"

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")([](){
        return "Hola, mundo";
    });

    app.port(18080).run();
}

Este código en C++ expone un sencillo servicio que imprime el famoso "Hola, mundo".

Requisitos para instalarlo:

  • Git.
  • Python (versión 3).
  • CMake.
  • VsCode con soporte a C++.
  • VCPKG.

Validamos si tenemos Git:

$ git --version

Validamos si tenemos Python:

$ python --version

Validamos si tenemos CMake:

$ cmake --version

Ahora instalaremos VCPKG. Abrimos una terminal y tecleamos este comando git para clonar el repo.

$ git clone https://github.com/microsoft/vcpkg.git

Nos ubicamos en la carpeta:

$ cd vcpkg

Ejecutamos estos archivos:

$ bootstrap-vcpkg.bat
$ vcpkg integrate install

Si todo va bien, clonaremos el repo de Crow (antes sal del directorio de vcpkg):

$ git clone https://github.com/CrowCpp/Crow.git

Nos ubicamos en el directorio de Crow:

$ cd Crow

Buscar el directorio de ``scripts``. Ejecuta estos comandos:

$ python merge_all.py ..\include crow_all.h

Si todo va bien, salimos del directorio de Crow y creamos un proyecto C++ vacío:

$ mkdir proyecto-crow
$ cd proyecto-crow

Copiamos los archivos ``crow_all.h``, ``example_with_all.cpp`` y ``vcpkg.json`` del directorio Crow al directorio del proyecto nuevo. Además agregamos ``crow_all.h`` como archivo de encabezado y ``example_with_all.cpp`` como archivo fuente.

El directorio quedará de este modo:

proyecto-crow/ 
   crow_all.h
   example_with_all.cpp
   vcpkg.json

Abrimos nuestro Visual Studio Code. Editamos el nombre del archivo C++ para denominarlo ``holamundo.cpp``. Sustituimos el contenido por este otro:

#include "crow.h"

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")([](){
        return "Hola, mundo";
    });

    app.port(18080).run();
}

Debemos hacer una instalación más. Nos ubicamos en el directorio de vcpkg y ejecutamos este comando:

$ vcpkg install asio

Lanzamos otro comando:

$ vcpkg integrate install

Con esto nos permitira instalar la librería asio. Necesaria para nuestra aplciación. Regresamos al directorio del proyecto (proyecto-crow) y creamos el ejecutable:

$ g++ holamundo.cpp -o holamundo.exe

Si todo va bien, creará el ejecutable. Otra forma es con Visual Studio Code. 

Abrimos el navegador en la ruta: http://localhost:18080/

Con el mensaje de "Hola, mundo".

Conclusión: este es uno de los 101010100 frameworks web que prometen la panacea universal; resolver todos los problemas que los demás frameworks prometieron hace miles de años. Se ve interesante, pero en el mundo laboral difícilmente se optaría abandonar algún desarrollo en, digamos Spring Boot, por este. Además de ser para C++. Para ello se optarían otros lenguajes como Java, Python o C#.

Enlaces:

https://crowcpp.org


sábado, 11 de octubre de 2025

Programando en Java no. 10: Consumir una API con la librería OkHttp

En el post anterior vimos como consumir una API desde C#. Ahora lo haremos con Java y OkHttp, un cliente HTTP que es muy eficiente y que ofrece:

  • La compatibilidad con HTTP/2 permite que todas las solicitudes al mismo host compartan un socket. 
  • La agrupación de conexiones reduce la latencia de las solicitudes (si HTTP/2 no está disponible). GZIP transparente reduce el tamaño de las descargas. 
  • El almacenamiento en caché de respuestas evita por completo que la red reciba solicitudes repetidas.

Además de seguir especificaciones HTTP modernas como:

  • Semántica HTTP - RFC 9110.
  • Almacenamiento en caché HTTP - RFC 9111 
  • HTTP/1.1 - RFC 9112 
  • HTTP/2 - RFC 9113 
  • WebSockets - RFC 6455
  • SSE - Eventos enviados por el servidor.

1. Comencemos creando el proyecto con Maven:

$ mvn archetype:generate \
        -DgroupId=com.inforhomex.app \
        -DartifactId=proyecto \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DarchetypeVersion=1.4 \
        -DinteractiveMode=false

Nos posicionamos en el directorio creado y modificamos la clase principal.

App.java

package com.inforhomex.app;

import okhttp3.*;
import java.io.IOException;
import static java.lang.System.out;
import static java.lang.System.err;

/**
 * @author Codemonkey Junior
 */
public class App {

    public static final String URL_STRING = "http://localhost:3000/api/saludo";
    public static void main(String[] args) {
        out.println("\t [ Consumiendo una API con okhttp ]");
        hacerPeticionEnApi(args);
    }

    private static void hacerPeticionEnApi(String... args){
        OkHttpClient client = new OkHttpClient();
        HttpUrl.Builder urlBuilder = HttpUrl.parse(URL_STRING).newBuilder();
        urlBuilder.addQueryParameter("id", "765421");
        final String finalUrl = urlBuilder.build().toString();
        Request.Builder requestBuilder = new Request.Builder()
        .url(finalUrl).method("GET", null)
        .addHeader("application-name", "application-java");
        Request request = requestBuilder.build();
        try(Response response = client.newCall(request).execute()){
            out.println(response.code());
            if(response.body() != null){
                out.println(response.body().string());
            }else{
                out.println("La respuesta es: null");
            }
        }catch(IOException ioex){
            err.printf("Excepcion: %s\n", ioex.getMessage());
        }

    }

}

En esta clase creamos un cliente gracias a la librería okhttp3 y en específico a la clase ``OkHttpClient``. Indicamos la URL del servicio (hecho en Node JS). Indicamos el valor a mandar (id) y el nombre de la aplicación en las cabeceras (header).

Compilamos la aplicación:

$ mvn clean compile

Reusaremos el servicio creado en el post anterior.

index.js

import express from 'express';


const app = express();
const port = 3000;

// Parsear JSON
app.use(express.json());

app.get('/', (req, res) => {
    res.send('Hola, mundo desde Node JS y Express.');
});



app.get('/api/saludo', (req, res) => {
  const id = req.query.id || 'desconocido';
  const appName = req.headers['application-name'] || 'sin nombre';

  res.json({
    mensaje: `¡Hola! Recibí tu ID: ${id}`,
    origen: `Aplicación: ${appName}`
  });
});



app.listen(port, () => {
    console.log(`Escuchando desde el puerto: http://localhost:${port}`);
});

Ejecutamos el proyecto node e iniciará el servicio en la url: http://localhost:3000

$ npm start

Ejecutamos la aplicación Java con Maven, pero antes debemos hacer unos ajustes al ``pom.xml``

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.inforhomex.app</groupId>
  <artifactId>proyecto-okhtpp</artifactId>
  <version>1.0</version>

  <name>proyecto-okhtpp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp-bom</artifactId>
        <version>5.2.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Optionally: parameterized tests support -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.squareup.okhttp/okhttp -->
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp-jvm</artifactId>
      <version>5.1.0</version>
    </dependency>

    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>mockwebserver3</artifactId>
    </dependency>

    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>logging-interceptor</artifactId>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>

        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>exec-maven-plugin</artifactId>
          <version>1.4.0</version>
          <configuration>
            <mainClass>com.inforhomex.app.App</mainClass>
          </configuration>
        </plugin>


        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.3.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.13.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.4.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>3.1.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.12.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.6.1</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Esto nos permitirá agregar las librerías y ejecutar la aplicación con Maven:

$ mvn exec:java

Si todo es correcto, veremos en consola el resultado:

         [ Consumiendo una API con okhttp ]
200
{"mensaje":"¡Hola! Recibí tu ID: 765421","origen":"Aplicación: application-java"}

¡Hemos consumido un servicio Node JS con Java y la librería okhttp!

Más ejemplos en próximas entregas.

Enlaces:

https://square.github.io/okhttp/


Programando en C# no. 11 (consumiendo una API)

En está ocasión veremos cómo consumir una API desde una aplicación hecha con C#.

Se tratará de una aplicación sencilla y sin usar un framework web.

Tendremos una aplicación Cliente y otra Servidor.

En este caso el cliente lo haremos con C# y la aplicación servidor con Node JS.

Comencemos.

1. Creamos una aplicación C# con dotnet.

$ mkdir proyecto-api
$ cd proyecto-api
$ dotnet new webapi

2. Modificamos el programa principal:

Program.cs

using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;

class Program
{
    static async Task Main(string[] args)
    {
        using (var client = new HttpClient())
        {
            var query = System.Web.HttpUtility.ParseQueryString(string.Empty);
            query["id"] = "1234";
            var uriBuilder = new UriBuilder("http://localhost:3000/api/saludo");
            uriBuilder.Query = query.ToString();
            var request = new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = uriBuilder.Uri
            };
            request.Headers.Add("application-name", "Aplicacion .NET");
            var response = await client.SendAsync(request);
            var responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseBody);
        }
    }
}

Este será nuestro programa cliente que accederá a un servicio de Node Js. Le mandaremos un id y el nombre de la aplicación (este en los encabezados). Colocaremos la URL del servicio y el método HTTP a usar, en este caso será el método GET, pues queremos obtener un resultado.

Ahora el servicio con Node JS.

1. Creamos el proyecto:

$ mkdir proyecto-rest
$ cd proyecto-rest
$ npm init -y

Esto nos generará un package.json. Lo editaremos más adelante. Ahora es necesario instalar Express JS en caso de no tenerlo.

$ npm install express

2. Creamos un archivo JS que será nuestro programa servidor.

index.js

import express from 'express';


const app = express();
const port = 3000;

// Parsear JSON
app.use(express.json());

app.get('/', (req, res) => {
    res.send('Hola, mundo desde Node JS y Express.');
});



app.get('/api/saludo', (req, res) => {
  const id = req.query.id || 'desconocido';
  const appName = req.headers['application-name'] || 'sin nombre';

  res.json({
    mensaje: `¡Hola! Recibí tu ID: ${id}`,
    origen: `Aplicación: ${appName}`
  });
});



app.listen(port, () => {
    console.log(`Escuchando desde el puerto: http://localhost:${port}`);
});

Nuestro servicio estará expuesto en la URL: http://localhost:3000/saludo

3. Editamos el archivo ``package.json``

{
  "name": "proyecto-rest",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Codemonkey Junior",
  "license": "ISC",
  "description": "Un sencillo servicio con Node JS",
  "type": "module",
  "dependencies": {
    "express": "^5.1.0"
  }
}

Ahora podremos ejecutar el proyecto Node JS con este comando:

$ npm start

Y ejecutamos el cliente:

$ dotnet build
$ dotnet run

Si todo va bien veremos esto en la consola:

{"mensaje":"¡Hola! Recibí tu ID: 1234","origen":"Aplicación: Aplicacion .NET"}

Con esto hemos creado nuestra aplicación que consume una sencilla API de Node JS.

Enlaces:

https://alquimistadecodigo.blogspot.com/2025/09/node-consumiendo-una-api-publica.html


Consumiendo una API pública con Kotlin & OkHttp

En el pasado post  vimos cómo consumir un servicio Node desde una aplicación Java gracias a la librería OkHttp . Esta vez usaremos Kotlin...

Etiquetas

Archivo del blog