Remote - Cliente HTTP
Introducción
A menudo, en nuestras aplicaciones, necesitamos hacer peticiones HTTP a servicios externos. Esto se puede hacer utilizando el recurso _remote.
Para realizar llamadas HTTP internamente a otros servicios o servidores internos en el equipo, o para llamar a algún servicio en internet, el uso del recurso Remote es esencial.
Consulte la documentación del recurso Remote.
Incluso podemos enviar y recibir datos en JSON y subir o descargar archivos.
Inicialización
Para poder realizar una llamada HTTP, primero debemos iniciarla; para ello utilizaremos el método init de _remote y luego guardaremos el resultado de esta operación en una variable llamada remoto para poder referenciar la petición:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
// Admite enviar parámetros HTTP
const remotoSimple = _remote.init();
// Admite enviar JSON:
const remotoJSON = _remote.init().asJSON();
// Admite enviar parámetros HTTP
const remotoSimple = _remote.init();
// Admite enviar JSON:
const remotoJSON = _remote.init().asJSON();
# Admite enviar parámetros HTTP
remotoSimple = _remote.init()
# Admite enviar JSON:
remotoJSON = _remote.init().asJSON()
# Admite enviar parámetros HTTP
remotoSimple = _remote.init()
# Admite enviar JSON:
remotoJSON = _remote.init().asJSON()
// Admite enviar parámetros HTTP
val remotoSimple = _remote.init()
// Admite enviar JSON:
val remotoJSON = _remote.init().asJSON()
// Admite enviar parámetros HTTP
final remotoSimple = _remote.init()
// Admite enviar JSON:
final remotoJSON = _remote.init().asJSON()
Configuración
También podemos definir configuraciones globales en la aplicación; observe este ejemplo completo:
...
"remote": {
"ejemplo-basic-auth": {
"url": "https://ejemplo.com/api/rest/ejemplo",
"form": true,
"authorization": {
"username": "admin",
"password": "clave"
},
"data": {
"param1": "valor 1...",
"param2": "valor 2..."
}
},
"ejemplo-auth-token": {
"urlPrefix": "https://ejemplo.com/api",
"json": true,
"authorization": "Bearer TOKEN-DE-AUTORIZACIÓN-AQUÍ",
"connectTimeout": 5000,
"readTimeout": 60000,
}
},
...
Podemos configurar un prefijo de URL (urlPrefix) o la URL completa (url).
Podemos definir si la petición, por defecto, debe enviar los datos como formulario o JSON.
Admite enviar autorización básica con usuario y contraseña o token.
Y es posible definir los datos predeterminados que se enviarán (data), así como los tiempos límite de conexión (connectTimeout) y de lectura (readTimeout).
Cómo usar
Para utilizar el recurso Remote con las configuraciones definidas en la aplicación, basta con iniciar el recurso con la clave de la configuración; por ejemplo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const respuesta = _remote.init("ejemplo-basic-auth").post();
const respuesta = _remote.init("ejemplo-basic-auth").post();
respuesta = _remote.init("ejemplo-basic-auth").post()
respuesta = _remote.init("ejemplo-basic-auth").post()
val respuesta = _remote.init("ejemplo-basic-auth").post()
final respuesta = _remote.init("ejemplo-basic-auth").post()
Y en caso de que se defina la URL de prefijo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init("ejemplo-auth-token");
const respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
);
const remoto = _remote.init("ejemplo-auth-token");
const respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
);
remoto = _remote.init("ejemplo-auth-token")
respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
)
remoto = _remote.init("ejemplo-auth-token")
respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
)
val remoto = _remote.init("ejemplo-auth-token")
val respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
)
final remoto = _remote.init("ejemplo-auth-token")
final respuesta = remoto.post(
"/services/carga-datos",
_val.map()
.set("param1", "valor 1")
.set("param2", "valor 2")
)
Petición HTTP
Vea cómo podemos iniciar una petición HTTP utilizando el recurso Remote.
Enviar JSON
Para enviar datos en formato JSON necesitamos indicar el tipo de contenido como JSON:
La forma sencilla de hacerlo es utilizando el método asJSON(), por ejemplo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init().asJSON();
remoto.setURL("http://ejemplo.com/guardar");
const respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
);
const remoto = _remote.init().asJSON();
remoto.setURL("http://ejemplo.com/guardar");
const respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
);
remoto = _remote.init().asJSON()
remoto.setURL("http://ejemplo.com/guardar")
respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
)
remoto = _remote.init().asJSON()
remoto.setURL("http://ejemplo.com/guardar")
respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
)
val remoto = _remote.init().asJSON()
remoto.setURL("http://ejemplo.com/guardar")
val respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
)
final remoto = _remote.init().asJSON()
remoto.setURL("http://ejemplo.com/guardar")
final respuesta = remoto.post(
_val.map()
.set("datos", "ejemplo")
.set("json", true)
)
Así, los datos se enviarán como JSON para los métodos HTTP POST y PUT, pero para GET y DELETE los datos siempre se envían en la querystring porque, para estos métodos, el protocolo HTTP no admite que se incluya un cuerpo con datos.
Métodos HTTP
No necesitamos indicar explícitamente en la cabecera de la llamada su método HTTP; para ello, nuestra referencia de la llamada dispone de una función propia para cada tipo de método HTTP:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init();
remoto.setURL("http://ejemplo.com");
remoto.get(); // para llamadas GET
remoto.post(); // para llamadas POST
remoto.put(); // para llamadas PUT
remoto.delete(); // para llamadas DELETE
const remoto = _remote.init();
remoto.setURL("http://ejemplo.com");
remoto.get(); // para llamadas GET
remoto.post(); // para llamadas POST
remoto.put(); // para llamadas PUT
remoto.delete(); // para llamadas DELETE
remoto = _remote.init()
remoto.setURL("http://ejemplo.com")
remoto.get() # para llamadas GET
remoto.post() # para llamadas POST
remoto.put() # para llamadas PUT
remoto.delete() # para llamadas DELETE
remoto = _remote.init()
remoto.setURL("http://ejemplo.com")
remoto.get() # para llamadas GET
remoto.post() # para llamadas POST
remoto.put() # para llamadas PUT
remoto.delete() # para llamadas DELETE
val remoto = _remote.init()
remoto.setURL("http://ejemplo.com")
remoto.get() // para llamadas GET
remoto.post() // para llamadas POST
remoto.put() // para llamadas PUT
remoto.delete() // para llamadas DELETE
final remoto = _remote.init()
remoto.setURL("http://ejemplo.com")
remoto.get() // para llamadas GET
remoto.post() // para llamadas POST
remoto.put() // para llamadas PUT
remoto.delete() // para llamadas DELETE
Cabecera HTTP
Para configurar los parámetros de la cabecera HTTP podemos definir las claves con el método getHeader(), por ejemplo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init();
remoto.getHeader().set("CLAVE", "VALOR");
const remoto = _remote.init();
remoto.getHeader().set("CLAVE", "VALOR");
remoto = _remote.init()
remoto.getHeader().set("CLAVE", "VALOR")
remoto = _remote.init()
remoto.getHeader().set("CLAVE", "VALOR")
val remoto = _remote.init()
remoto.getHeader().set("CLAVE", "VALOR")
final remoto = _remote.init()
remoto.getHeader().set("CLAVE", "VALOR")
URL de la petición
Para indicar la URL de la petición basta con pasarla como primer parámetro de las funciones de los respectivos métodos HTTP:
const remoto = _remote.init().asJSON()
remoto.get("http://ejemplo.com") // para llamadas GET
remoto.post("http://ejemplo.com") // para llamadas POST
remoto.put("http://ejemplo.com") // para llamadas PUT
remoto.delete("http://ejemplo.com") // para llamadas DELETE
Añadir datos a la petición
Si necesita añadir datos al cuerpo de la petición, basta con pasarlos como segundo parámetro de la función del método correspondiente:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init().asJSON();
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas DELETE
const remoto = _remote.init().asJSON();
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)); // para llamadas DELETE
remoto = _remote.init().asJSON()
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas DELETE
remoto = _remote.init().asJSON()
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)) # para llamadas DELETE
val remoto = _remote.init().asJSON()
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas DELETE
final remoto = _remote.init().asJSON()
remoto.get("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas GET
remoto.post("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas POST
remoto.put("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas PUT
remoto.delete("http://ejemplo.com", _val.map().set("id_usuario", 1)) // para llamadas DELETE
Respuesta
Después de recibir la respuesta de la petición podemos leer sus datos utilizando el método json o content:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remoto = _remote.init().asJSON();
const respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
);
if (respuesta.ok()) {
_log.info("Respuesta en JSON", respuesta.json());
} else {
_log.info("Falló con el estado: "+ respuesta.statusCode(), respuesta.content());
}
const remoto = _remote.init().asJSON();
const respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
);
if (respuesta.ok()) {
_log.info("Respuesta en JSON", respuesta.json());
} else {
_log.info("Falló con el estado: "+ respuesta.statusCode(), respuesta.content());
}
remoto = _remote.init().asJSON()
respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
)
if respuesta.ok():
_log.info("Respuesta en JSON", respuesta.json())
else:
_log.info(
f"Falló con el estado: {respuesta.statusCode()}",
respuesta.content()
)
remoto = _remote.init().asJSON()
respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
)
if respuesta.ok()
_log.info("Respuesta en JSON", respuesta.json())
else
_log.info(
"Falló con el estado: #{respuesta.statusCode()}",
respuesta.content()
)
end
val remoto = _remote.init().asJSON()
val respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
)
if (respuesta.ok()) {
_log.info("Respuesta en JSON", respuesta.json())
} else {
_log.info(
"Falló con el estado: ${respuesta.statusCode()}",
respuesta.content()
)
}
final remoto = _remote.init().asJSON()
final respuesta = remoto.post(
"http://ejemplo.com",
_val.map().set("id_usuario", 1)
)
if (respuesta.ok()) {
_log.info("Respuesta en JSON", respuesta.json())
} else {
_log.info("Falló con el estado: "+ respuesta.statusCode(), respuesta.content())
}
En la respuesta, con la función json() obtenemos el array o el objeto proporcionado como Values, y con la función content()
obtenemos contenido en texto sin procesar o el HTML; el número del código de estado recibido viene en statusCode(), mientras que ok()
devuelve un valor booleano que es verdadero si el código de estado está dentro del intervalo entre 200 y 299.
Descarga
Para descargar archivos utilizamos Remote con asBinary() para indicar que
debe ser una transferencia binaria.
Vea este ejemplo de servicio completo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const descarga = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!descarga.ok()) {
_log.error(`Estado de la descarga del archivo: ${descarga.statusCode()}\n${descarga.toString()}`);
_header.status(500);
_out.json({resultado: false, error: 'descarga-archivo-fallida'});
_exec.stop();
}
const archivo = descarga.file();
_log.info(`Nombre del archivo descargado: ${archivo.name()}`);
archivo.save(_app.file(`storage/filesystem/server/${archivo.name()}`));
archivo.save(_storage.filesystem("server", "otro.png").file());
archivo.save(_os.file("/tmp/descarga.png"));
_out.json({resultado: true});
const descarga = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!descarga.ok()) {
_log.error(`Estado de la descarga del archivo: ${descarga.statusCode()}\n${descarga.toString()}`);
_header.status(500);
_out.json({resultado: false, error: 'descarga-archivo-fallida'});
_exec.stop();
}
const archivo = descarga.file();
_log.info(`Nombre del archivo descargado: ${archivo.name()}`);
archivo.save(_app.file(`storage/filesystem/server/${archivo.name()}`));
archivo.save(_storage.filesystem("server", "otro.png").file());
archivo.save(_os.file("/tmp/descarga.png"));
_out.json({resultado: true});
descarga = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if not descarga.ok():
_log.error(f"Estado de la descarga del archivo: {descarga.statusCode()}\n{descarga.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", False).set("error", "descarga-archivo-fallida"))
_exec.stop()
archivo = descarga.file()
_log.info(f"Nombre del archivo descargado: {archivo.name()}")
archivo.save(_app.file(f"storage/filesystem/server/{archivo.name()}"))
archivo.save(_storage.filesystem("server", "otro.png").file())
archivo.save(_os.file("/tmp/descarga.png"))
_out.json(_val.map().set("resultado", True))
descarga = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
unless descarga.ok()
_log.error("Estado de la descarga del archivo: #{descarga.statusCode()}\n#{descarga.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", false).set("error", "descarga-archivo-fallida"))
_exec.stop()
end
archivo = descarga.file()
_log.info("Nombre del archivo descargado: #{archivo.name()}")
archivo.save(_app.file("storage/filesystem/server/#{archivo.name()}"))
archivo.save(_storage.filesystem("server", "otro.png").file())
archivo.save(_os.file("/tmp/descarga.png"))
_out.json(_val.map().set("resultado", true))
val descarga = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if (!descarga.ok()) {
_log.error("Estado de la descarga del archivo: ${descarga.statusCode()}\n${descarga.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", false).set("error", "descarga-archivo-fallida"))
_exec.stop()
}
archivo = descarga.file()
_log.info("Nombre del archivo descargado: ${archivo.name()}")
archivo.save(_app.file("storage/filesystem/server/${archivo.name()}"))
archivo.save(_storage.filesystem("server", "otro.png").file())
archivo.save(_os.file("/tmp/descarga.png"))
_out.json(_val.map().set("resultado", true))
final descarga = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png')
if (!descarga.ok()) {
_log.error(`Estado de la descarga del archivo: ${descarga.statusCode()}\n${descarga.toString()}`)
_header.status(500)
_out.json({resultado: false, error: 'descarga-archivo-fallida'})
_exec.stop()
}
final archivo = descarga.file()
_log.info(`Nombre del archivo descargado: ${archivo.name()}`)
archivo.save(_app.file(`storage/filesystem/server/${archivo.name()}`))
archivo.save(_storage.filesystem("server", "otro.png").file())
archivo.save(_os.file("/tmp/descarga.png"))
_out.json({resultado: true})
El
archivo.savese utiliza 3 veces a propósito; para saber más sobre_app.file,_storagey_os.file, consulte el tutorial sobre Manipulación de Archivos y Carpetas.
Subida
Para enviar un archivo estructurado como formulario en formato multipart, utilizamos asMultipartFormData().
Vea el ejemplo completo de un servicio:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const archivo = _app.file("public/imagenes/logo.png");
const subida = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/receptor-archivo',
_val.map()
.set("archivo", archivo)
);
if (subida.ok()) {
_log.info(`Subida correcta: ${archivo.name()}`);
_out.json({resultado: true});
} else {
_log.error(`Estado de la subida del archivo: ${subida.statusCode()}\n${subida.toString()}`);
_header.status(500);
_out.json({resultado: false, error: 'subida-archivo-fallida'});
}
const archivo = _app.file("public/imagenes/logo.png");
const subida = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/receptor-archivo',
_val.map()
.set("archivo", archivo)
);
if (subida.ok()) {
_log.info(`Subida correcta: ${archivo.name()}`);
_out.json({resultado: true});
} else {
_log.error(`Estado de la subida del archivo: ${subida.statusCode()}\n${subida.toString()}`);
_header.status(500);
_out.json({resultado: false, error: 'subida-archivo-fallida'});
}
archivo = _app.file("public/imagenes/logo.png")
subida = _remote.asMultipartFormData().post(
"http://localhost:9000/services/receptor-archivo",
_val.map().set("archivo", archivo)
)
if subida.ok():
_log.info(f"Subida correcta: {archivo.name()}")
_out.json(_val.map().set("resultado", True))
else:
_log.error(f"Estado de la subida del archivo: {subida.statusCode()}\n{subida.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", False).set("error", "subida-archivo-fallida"))
archivo = _app.file("public/imagenes/logo.png")
subida = _remote.asMultipartFormData().post(
"http://localhost:9000/services/receptor-archivo",
_val.map().set("archivo", archivo)
)
if subida.ok()
_log.info("Subida correcta: #{archivo.name()}")
_out.json(_val.map().set("resultado", true))
else
_log.error("Estado de la subida del archivo: #{subida.statusCode()}\n#{subida.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", false).set("error", "subida-archivo-fallida"))
end
val archivo = _app.file("public/imagenes/logo.png")
val subida = _remote.asMultipartFormData().post(
"http://localhost:9000/services/receptor-archivo",
_val.map().set("archivo", archivo)
)
if (subida.ok()) {
_log.info("Subida correcta: ${archivo.name()}")
_out.json(_val.map().set("resultado", true))
} else {
_log.error("Estado de la subida del archivo: ${subida.statusCode()}\n${subida.toString()}")
_header.status(500)
_out.json(_val.map().set("resultado", false).set("error", "subida-archivo-fallida"))
}
final archivo = _app.file("public/imagenes/logo.png")
final subida = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/receptor-archivo',
_val.map()
.set("archivo", archivo)
)
if (subida.ok()) {
_log.info(`Subida correcta: ${archivo.name()}`)
_out.json({resultado: true})
} else {
_log.error(`Estado de la subida del archivo: ${subida.statusCode()}\n${subida.toString()}`)
_header.status(500)
_out.json({resultado: false, error: 'subida-archivo-fallida'})
}
Observe que en el ejemplo anterior el archivo se envía a otro servicio; vea cómo se puede implementar en Netuno este otro servicio que recibe el archivo:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const archivo = _req.getFile("archivo");
_log.info(`Nombre del archivo recibido: ${archivo.name()}`);
archivo.save(_storage.filesystem("server", "recibido.png").file());
_out.json({resultado: true});
const archivo = _req.getFile("archivo");
_log.info(`Nombre del archivo recibido: ${archivo.name()}`);
archivo.save(_storage.filesystem("server", "recibido.png").file());
_out.json({resultado: true});
archivo = _req.getFile("archivo")
_log.info(f"Nombre del archivo recibido: {archivo.name()}")
archivo.save(_storage.filesystem("server", "recibido.png").file())
_out.json(_val.map().set("resultado", True))
archivo = _req.getFile("archivo")
_log.info("Nombre del archivo recibido: #{archivo.name()}")
archivo.save(_storage.filesystem("server", "recibido.png").file())
_out.json(_val.map().set("resultado", true))
val archivo = _req.getFile("archivo")
_log.info("Nombre del archivo recibido: ${archivo.name()}")
archivo.save(_storage.filesystem("server", "recibido.png").file())
_out.json(_val.map().set("resultado", true))
final archivo = _req.getFile("archivo")
_log.info(`Nombre del archivo recibido: ${archivo.name()}`)
archivo.save(_storage.filesystem("server", "recibido.png").file())
_out.json({resultado: true})
Conclusión
Con el recurso Remote podemos integrar API externas, podemos exportar, importar o sincronizar datos.
Incluso podemos integrar servicios implementados en otras tecnologías, lo que permite realizar operaciones específicas proporcionadas por otros lenguajes.