Node
Tutorial Node: HTTP params, headers y body
Node.js: Aprende a manejar parámetros de URL y cadenas de consulta en peticiones HTTP. Desarrolla rutas dinámicas seguras y eficientes en tu servidor.
Aprende Node GRATIS y certifícateLectura de parámetros de URL
Al desarrollar un servidor HTTP en Node.js, es esencial entender cómo acceder a los parámetros de la URL que llegan en las peticiones. Estos parámetros pueden encontrarse en el path de la URL o en la cadena de consulta (query string).
Para manejar estos datos, Node.js proporciona la clase global URL
, que permite analizar y manipular fácilmente la URL de una petición. Al recibir una solicitud, podemos crear una instancia de URL
pasando como argumentos la URL requerida y el origen base.
import { createServer } from "http";
const servidor = createServer((req, res) => {
const miURL = new URL(req.url, `http://${req.headers.host}`);
// Ahora podemos acceder a las diferentes partes de la URL
});
servidor.listen(3000, () => {
console.log("Servidor escuchando en el puerto 3000");
});
Para acceder a los parámetros de consulta, utilizamos la propiedad searchParams
de la instancia URL
, que es un objeto de tipo URLSearchParams
. Este objeto ofrece métodos como get()
, getAll()
, has()
y keys()
para interactuar con los parámetros.
import { createServer } from "http";
const servidor = createServer((req, res) => {
const miURL = new URL(req.url, `http://${req.headers.host}`);
const parametros = miURL.searchParams;
const categoria = parametros.get("categoria"); // Obtener valor de 'categoria'
const orden = parametros.get("orden"); // Obtener valor de 'orden'
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`Categoría: ${categoria}, Orden: ${orden}`);
});
servidor.listen(3000, () => {
console.log("Servidor escuchando en el puerto 3000");
});
Si un cliente realiza una petición a http://localhost:3000/productos?categoria=libros&orden=asc
, el servidor responderá con los valores de categoría y orden extraídos de la URL.
Para gestionar parámetros incluidos en el path, podemos usar la propiedad pathname
de la instancia URL
. Esto es útil cuando los recursos se identifican por rutas dinámicas.
import { createServer } from "http";
const servidor = createServer((req, res) => {
const miURL = new URL(req.url, `http://${req.headers.host}`);
const ruta = miURL.pathname; // Por ejemplo: '/usuarios/45/perfil'
const segmentos = ruta.split("/").filter((segmento) => segmento !== "");
if (segmentos[0] === "usuarios" && segmentos[2] === "perfil") {
const idUsuario = segmentos[1]; // Extrae '45' de la ruta
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`Perfil del usuario con ID: ${idUsuario}`);
} else {
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Recurso no encontrado");
}
});
servidor.listen(3000, () => {
console.log("Servidor escuchando en el puerto 3000");
});
En este código, se analizan los segmentos del path para determinar la acción a realizar y extraer el ID del usuario. Esto permite crear rutas más semánticas y es una práctica común en el diseño de APIs RESTful.
Es recomendable también manejar los casos en que los parámetros podrían no estar presentes o contener valores inesperados. Implementar verificaciones y manejos de error adecuados contribuye a la robustez de la aplicación.
Además, para manejar múltiples parámetros o rutas más complejas, podríamos implementar una función de enrutamiento personalizada que dirija las peticiones al controlador correspondiente, mejorando la organización y mantenibilidad del código.
Parseo de body (JSON, form-urlencoded, etc.)
En aplicaciones web desarrolladas con Node.js, es común que las peticiones HTTP contengan datos en el cuerpo (body) de la solicitud, especialmente en métodos como POST y PUT. Para procesar estos datos, es fundamental saber cómo leer y parsear el body de la petición, adaptándose al tipo de contenido que pueda incluir, como JSON o form-urlencoded.
Al utilizar el módulo http
de Node.js, el cuerpo de la petición no está disponible directamente como una propiedad de req
. En su lugar, debemos gestionar el flujo de datos que llega en forma de streams. Los datos se reciben en fragmentos (chunks), y debemos concatenarlos para obtener el body completo. A continuación se muestra cómo leer el body de una petición:
import { createServer } from "http";
const servidor = createServer((req, res) => {
let datos = "";
req.on("data", (chunk) => {
datos += chunk;
});
req.on("end", () => {
// Aquí procesamos los datos recibidos
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Datos recibidos y procesados correctamente");
});
});
servidor.listen(3000, () => {
console.log("Servidor escuchando en el puerto 3000");
});
En este ejemplo, utilizamos los eventos 'data'
y 'end'
del objeto req
para recopilar los datos del body. Es importante manejar el evento 'data'
para ir acumulando los fragmentos que llegan y el evento 'end'
para saber cuándo ha terminado de llegar toda la información.
Para parsear el body según el tipo de contenido, debemos identificar el Content-Type de la petición. Podemos obtener este valor accediendo a la cabecera correspondiente:
const tipoContenido = req.headers['content-type'];
Si el Content-Type
es 'application/json'
, significa que el body está en formato JSON y podemos parsearlo utilizando JSON.parse()
:
req.on("end", () => {
const tipoContenido = req.headers["content-type"];
if (tipoContenido === "application/json") {
try {
const objetoJSON = JSON.parse(datos);
// Trabajar con objetoJSON
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ mensaje: "JSON procesado correctamente" }));
} catch (error) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "JSON inválido" }));
}
} else {
// Manejo de otros tipos de contenido
}
});
Es fundamental manejar las excepciones al parsear JSON, ya que si el cliente envía un JSON mal formado, JSON.parse()
lanzará un error. Al capturar este error, podemos responder adecuadamente con un código de estado 400 indicando un Bad Request.
Si el Content-Type
es 'application/x-www-form-urlencoded'
, típico de formularios HTML, podemos utilizar la clase URLSearchParams
para convertir la cadena recibida en un objeto de fácil manipulación:
req.on("end", () => {
const tipoContenido = req.headers["content-type"];
if (tipoContenido === "application/x-www-form-urlencoded") {
const parametros = new URLSearchParams(datos);
const nombre = parametros.get("nombre");
const correo = parametros.get("correo");
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`Nombre: ${nombre}, Correo: ${correo}`);
} else {
// Manejo de otros tipos de contenido
}
});
URLSearchParams
nos permite acceder a los valores de los campos enviados en el formulario de manera sencilla, utilizando el método get()
. Esto facilita el procesamiento de los datos sin necesidad de utilizar librerías externas.
Es posible que necesitemos manejar varios tipos de contenido. Para ello, podemos implementar una lógica que determine el método de parseo adecuado según el Content-Type:
req.on("end", () => {
const tipoContenido = req.headers["content-type"];
if (tipoContenido === "application/json") {
// Parsear como JSON
} else if (tipoContenido === "application/x-www-form-urlencoded") {
// Parsear como form-urlencoded
} else {
res.writeHead(415, { "Content-Type": "text/plain" });
res.end("Tipo de contenido no soportado");
}
});
En este ejemplo, si el tipo de contenido no es soportado, respondemos con un código de estado 415 (Unsupported Media Type), lo cual es una práctica recomendada para indicar al cliente que el servidor no puede procesar el formato del body.
Cuando se esperan datos de gran tamaño, es crucial considerar aspectos de seguridad y rendimiento. Podemos implementar un límite en el tamaño del body para evitar posibles ataques de denegación de servicio (DoS):
let datos = "";
const limite = 1e6; // 1 MB
req.on("data", (chunk) => {
datos += chunk;
if (datos.length > limite) {
res.writeHead(413, { "Content-Type": "text/plain" });
res.end("El cuerpo de la petición es demasiado grande");
req.destroy();
}
});
De esta manera, si el tamaño de los datos recibidos supera el límite establecido, el servidor interrumpe la conexión y responde con un código de estado 413 (Payload Too Large), protegiéndose contra cargas excesivas.
Para manejar otros tipos de contenido, como multipart/form-data utilizado en la subida de archivos, se requiere un parseo más complejo. Aunque es posible implementar un parser manualmente, suele ser más práctico utilizar módulos externos especializados. Sin embargo, al trabajar sin frameworks, debemos ser cautelosos y considerar la complejidad añadida.
Manejo de headers de petición y respuesta en Node.js
En aplicaciones web, los headers de las peticiones y respuestas HTTP son fundamentales para transmitir metadatos entre el cliente y el servidor. En Node.js, manipular estos headers es esencial para controlar aspectos como la autenticación, el formato de los datos y la caché.
Para acceder a los headers de la petición, podemos utilizar la propiedad headers
del objeto req
proporcionado por el módulo http
. Este objeto es un diccionario que contiene todos los headers enviados por el cliente en minúsculas.
import { createServer } from "http";
const servidor = createServer((req, res) => {
const headersPeticion = req.headers;
const agenteUsuario = headersPeticion["user-agent"];
const tipoContenido = headersPeticion["content-type"];
console.log("Headers de la petición:", headersPeticion);
console.log("Agente de usuario:", agenteUsuario);
console.log("Tipo de contenido:", tipoContenido);
res.end("Headers de la petición recibidos");
});
servidor.listen(3000, () => {
console.log("Servidor ejecutándose en el puerto 3000");
});
En este ejemplo, accedemos a headers comunes como User-Agent y Content-Type, que proporcionan información sobre el navegador del cliente y el tipo de datos enviados, respectivamente.
Para establecer los headers de la respuesta, utilizamos el método setHeader()
del objeto res
. Este método nos permite definir headers personalizados antes de enviar la respuesta al cliente.
import { createServer } from "http";
const servidor = createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
res.setHeader("X-Powered-By", "Node.js");
const respuesta = { mensaje: "Hola desde el servidor" };
res.end(JSON.stringify(respuesta));
});
servidor.listen(3000, () => {
console.log("Servidor ejecutándose en el puerto 3000");
});
Aquí, establecemos el header Content-Type a 'application/json'
para indicar que el cuerpo de la respuesta será un JSON. También añadimos un header personalizado X-Powered-By que informa sobre la tecnología utilizada.
Es importante tener en cuenta que los headers deben configurarse antes de enviar cualquier dato con res.write()
o res.end()
. Intentar modificar los headers después de enviar datos provocará un error.
Para obtener un header específico de la petición de forma segura, es recomendable acceder al valor normalizando el nombre del header. Dado que todos los nombres en req.headers
están en minúsculas, debemos asegurarnos de utilizar el mismo caso:
const tipoAutorizacion = req.headers['authorization'];
if (tipoAutorizacion) {
// Procesar token de autorización
} else {
res.statusCode = 401;
res.end('Autorización requerida');
}
Al manejar la autenticación, el header Authorization es crucial. En este ejemplo, verificamos si el header está presente para permitir o denegar el acceso.
Para enviar cookies al cliente, establecemos el header Set-Cookie en la respuesta. Podemos enviar múltiples cookies utilizando un array:
res.setHeader('Set-Cookie', [
'usuario=Juan; HttpOnly',
'tema=oscuro; Max-Age=3600'
]);
Las cookies permiten almacenar información en el navegador del cliente, y mediante atributos como HttpOnly y Max-Age, controlamos su seguridad y duración.
Al momento de redirigir al cliente a otra URL, utilizamos el header Location junto con el código de estado HTTP correspondiente (generalmente 301 o 302):
res.statusCode = 302;
res.setHeader('Location', 'https://www.ejemplo.com/nueva-ruta');
res.end();
Esta técnica es útil para rutas que han cambiado o para gestionar acceso a recursos protegidos.
Para controlar la caché en el navegador y proxies intermedios, configuramos headers como Cache-Control, Expires y ETag:
res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache por un día
Esto mejora el rendimiento al permitir que los clientes almacenen recursos estáticos localmente.
En ocasiones, es necesario obtener el valor de un header inmediatamente después de establecerlo. Podemos utilizar el método getHeader()
para este propósito:
res.setHeader('Content-Language', 'es');
const idioma = res.getHeader('Content-Language');
console.log('Idioma establecido:', idioma);
Si necesitamos verificar si un header específico ha sido enviado por el cliente, podemos comprobar su existencia:
if ('accept-language' in req.headers) {
const preferenciaIdioma = req.headers['accept-language'];
// Procesar preferencia de idioma
}
Para eliminar un header previamente establecido en la respuesta, utilizamos removeHeader()
:
res.setHeader('X-Obsoleto', 'valor');
res.removeHeader('X-Obsoleto');
Esto es útil si la lógica de la aplicación decide que un header ya no es necesario antes de enviar la respuesta.
Es fundamental ser consciente de los headers sensibles que no deben ser expuestos o manipulados indebidamente. Por ejemplo, los headers relacionados con la seguridad, como Strict-Transport-Security o Content-Security-Policy, deben configurarse con cuidado:
res.setHeader('Strict-Transport-Security', 'max-age=63072000; includeSubDomains');
Este header obliga al navegador a utilizar siempre HTTPS para las peticiones futuras, fortaleciendo la seguridad de la aplicación.
Al diseñar APIs, es común utilizar headers personalizados para funcionalidades específicas. Estos headers suelen comenzar con el prefijo X- o una nomenclatura propia:
res.setHeader('X-Request-ID', '12345');
El uso de headers personalizados debe ser consistente y documentado para garantizar su correcta utilización por parte de los clientes de la API.
Finalmente, al trabajar con Node.js es posible acceder a la versión en crudo de los headers tal y como fueron enviados, utilizando la propiedad rawHeaders
de req
:
console.log('Headers sin procesar:', req.rawHeaders);
Esta propiedad devuelve un array que alterna nombres y valores de los headers, preservando el caso original y el orden, lo cual puede ser útil para ciertas aplicaciones.
Manejar adecuadamente los headers de petición y respuesta en Node.js es esencial para construir aplicaciones web robustas, seguras y eficientes. Comprender cómo acceder y manipular estos metadatos permite un mayor control sobre la comunicación entre el cliente y el servidor.
Todas las lecciones de Node
Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Node.js
Introducción Y Entorno
Fundamentos Del Entorno Node.js
Introducción Y Entorno
Módulo Http Y Https
Http Y Api Rest
Http Params, Headers Y Body
Http Y Api Rest
Validación De Datos
Http Y Api Rest
Conexión A Bases De Datos Sin Orm
Persistencia
Creación De Consultas Básicas (Crud) Sin Orm
Persistencia
Módulo Fs
Sistema De Archivos
Introducción A La Seguridad
Seguridad
Sesiones Y Cookies
Seguridad
Roles Y Permisos
Seguridad
Testing En Node.js
Testing
Estructura De Carpetas
Arquitectura
Configuración Y Variables De Entorno
Arquitectura
En esta lección
Objetivos de aprendizaje de esta lección
- Entender qué son los parámetros de URL y cómo se utilizan en las peticiones HTTP.
- Aprender a acceder a los parámetros de consulta y del path en Node.js usando la clase global
URL
. - Conocer los métodos del objeto
URLSearchParams
para interactuar con los parámetros de consulta. - Implementar lógica para manejar rutas dinámicas y extraer información del path.
- Desarrollar enrutamiento simple para organizar el código del servidor.
- Resolver posibles fallos y manipular correctamente errores comunes al manejar URL.