expressed-example-app 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/app.js +44 -97
- package/changelog-core-enrutador.md +177 -0
- package/controllers/mainController.js +34 -0
- package/controllers/productController.js +22 -0
- package/controllers/userController.js +20 -0
- package/documentacion-bug-enrutamiento.md +114 -0
- package/hooks-config.js +42 -0
- package/hooks-personalizados.js +54 -0
- package/package.json +9 -48
- package/routes/main-routes.json +37 -0
- package/test-hooks.js +31 -0
- package/test-routes.json +9 -0
- package/config/routes.json +0 -70
- package/controllers/EndpointController.js +0 -43
- package/controllers/HomeController.js +0 -19
- package/controllers/ProductController.js +0 -47
- package/controllers/QueueController.js +0 -26
- package/controllers/UserController.js +0 -73
- package/hooks/AppHooks.js +0 -60
- package/middleware/bodyCapture.js +0 -20
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Express-Router-BDS Example Application
|
|
2
|
+
|
|
3
|
+
This is a sample Express.js application demonstrating the usage of the `express-router-bds` routing system with custom hooks and advanced features.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This example application showcases:
|
|
8
|
+
|
|
9
|
+
- High-performance routing with `express-router-bDS`
|
|
10
|
+
- JSON-based route configuration
|
|
11
|
+
- Custom hook system implementation
|
|
12
|
+
- Parameterized route handling
|
|
13
|
+
- Directory-based route loading
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Modular Route Configuration**: Routes defined in JSON files for easy maintenance
|
|
18
|
+
- **Custom Hooks**: Comprehensive hook system with `pre_route_load`, `post_route_load`, `before_route_handler`, `after_route_handler`, `route_handler_error`, and more
|
|
19
|
+
- **Parameterized Routes**: Support for dynamic routes like `/users/:id` and `/products/:category/:id`
|
|
20
|
+
- **Extensible Architecture**: Easy to extend with custom middleware and hooks
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
express-app/
|
|
26
|
+
├── app.js # Main application entry point
|
|
27
|
+
├── hooks-config.js # Base hook configurations
|
|
28
|
+
├── hooks-personalizados.js # Custom hook implementations
|
|
29
|
+
├── controllers/
|
|
30
|
+
│ ├── mainController.js # Home and init route handlers
|
|
31
|
+
│ ├── productController.js # Product route handlers
|
|
32
|
+
│ └── userController.js # User route handlers
|
|
33
|
+
├── routes/
|
|
34
|
+
│ └── main-routes.json # Route definitions in JSON format
|
|
35
|
+
└── node_modules/
|
|
36
|
+
└── express-router-bds/ # Custom routing system
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Available Endpoints
|
|
40
|
+
|
|
41
|
+
- `GET /` - Home page with welcome message
|
|
42
|
+
- `GET /init` - Initialization endpoint
|
|
43
|
+
- `GET /users/:id` - Get user by ID
|
|
44
|
+
- `GET /products/:category/:id` - Get product by category and ID
|
|
45
|
+
- `GET /test-hook` - Test endpoint for hook demonstration
|
|
46
|
+
|
|
47
|
+
## Hook System
|
|
48
|
+
|
|
49
|
+
The application demonstrates several types of hooks:
|
|
50
|
+
|
|
51
|
+
- `pre_route_load` - Executes before loading routes from a file
|
|
52
|
+
- `post_route_load` - Executes after loading routes from a file
|
|
53
|
+
- `before_route_processing` - Executes before processing any route
|
|
54
|
+
- `route_matched` - Executes when a matching route is found
|
|
55
|
+
- `before_route_handler` - Executes before executing a route handler
|
|
56
|
+
- `after_route_handler` - Executes after executing a route handler
|
|
57
|
+
- `route_handler_error` - Executes when a route handler error occurs
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
Start the server:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm start
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The application will be available at `http://localhost:3000`
|
|
74
|
+
|
|
75
|
+
## Testing
|
|
76
|
+
|
|
77
|
+
Test the endpoints:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
curl http://localhost:3000
|
|
81
|
+
curl http://localhost:3000/users/123
|
|
82
|
+
curl http://localhost:3000/products/electronics/456
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
This project is part of the `express-router-bds` ecosystem and follows the same licensing terms.
|
package/app.js
CHANGED
|
@@ -1,115 +1,62 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Aplicación principal
|
|
3
|
-
* Integración de @hooked, @router y @queued con una arquitectura limpia
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
const express = require('express');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const ExpressHooked = require('express-hooked');
|
|
10
2
|
const ExpressRouterIntegration = require('express-router-bds');
|
|
11
|
-
const
|
|
12
|
-
const AppHooks = require('./hooks
|
|
13
|
-
const
|
|
3
|
+
const RouteDirectoryLoader = require('express-router-bds/RouteDirectoryLoader');
|
|
4
|
+
const AppHooks = require('./hooks-config');
|
|
5
|
+
const HooksPersonalizados = require('./hooks-personalizados');
|
|
14
6
|
|
|
15
|
-
// Crear la aplicación Express
|
|
16
7
|
const app = express();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
// Leer la configuración de rutas desde el archivo JSON
|
|
20
|
-
const routesConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/routes.json'), 'utf8'));
|
|
8
|
+
const PORT = process.env.PORT || 3000;
|
|
21
9
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
10
|
+
// Crear instancia del router
|
|
11
|
+
const routerIntegration = new ExpressRouterIntegration();
|
|
24
12
|
|
|
25
|
-
// Inicializar
|
|
26
|
-
const
|
|
13
|
+
// Inicializar hooks y filtros
|
|
14
|
+
const appHooks = new AppHooks(app, routerIntegration.getHooks());
|
|
27
15
|
|
|
28
|
-
// Inicializar
|
|
29
|
-
const
|
|
16
|
+
// Inicializar hooks personalizados
|
|
17
|
+
const hooksPersonalizados = new HooksPersonalizados(routerIntegration);
|
|
30
18
|
|
|
31
|
-
// Registrar los hooks de la aplicación
|
|
32
|
-
AppHooks.registerHooks(hooked);
|
|
33
19
|
|
|
34
|
-
//
|
|
35
|
-
|
|
20
|
+
// Cargar rutas desde directorio
|
|
21
|
+
const routeDirectoryLoader = new RouteDirectoryLoader();
|
|
36
22
|
|
|
37
|
-
// Middleware para servir archivos estáticos
|
|
38
|
-
app.use(express.static(path.join(__dirname, 'public')));
|
|
39
23
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Construir la ruta del archivo del controlador
|
|
46
|
-
const controllerPath = path.join(__dirname, 'controllers', `${controllerName}.js`);
|
|
47
|
-
|
|
48
|
-
// Verificar si el archivo del controlador existe
|
|
49
|
-
if (fs.existsSync(controllerPath)) {
|
|
50
|
-
// Importar el controlador
|
|
51
|
-
const Controller = require(controllerPath);
|
|
52
|
-
|
|
53
|
-
// Verificar si la acción existe en el controlador
|
|
54
|
-
if (Controller[actionName]) {
|
|
55
|
-
// Registrar la ruta con el método HTTP, la ruta y el controlador
|
|
56
|
-
router.addRoute(route.method, route.path, (req, res) => {
|
|
57
|
-
// Disparar hook antes de ejecutar la acción
|
|
58
|
-
hooked.doAction('before_controller_action', req, res, route);
|
|
59
|
-
|
|
60
|
-
// Disparar hook para mostrar información detallada de la solicitud
|
|
61
|
-
// Usar el body capturado en lugar del body original que puede estar vacío
|
|
62
|
-
if (req.capturedBody) {
|
|
63
|
-
req.body = req.capturedBody;
|
|
64
|
-
}
|
|
65
|
-
hooked.doAction('request_details_log', req, res, route);
|
|
66
|
-
|
|
67
|
-
// Ejecutar la acción del controlador
|
|
68
|
-
Controller[actionName](req, res);
|
|
69
|
-
|
|
70
|
-
// Disparar hook después de ejecutar la acción
|
|
71
|
-
hooked.doAction('after_controller_action', req, res, route);
|
|
72
|
-
});
|
|
73
|
-
} else {
|
|
74
|
-
console.error(`La acción "${actionName}" no existe en el controlador "${controllerName}"`);
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
console.error(`El archivo del controlador "${controllerPath}" no existe`);
|
|
24
|
+
routerIntegration.addHook('route_matched', (matchedRoute, req, res) => {
|
|
25
|
+
console.log(`Ruta encontrada: ${matchedRoute.route.path}`);
|
|
26
|
+
if (matchedRoute.params && Object.keys(matchedRoute.params).length > 0) {
|
|
27
|
+
console.log(`Parámetros:`, matchedRoute.params);
|
|
78
28
|
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Aplicar el middleware de colas para inyectar el sistema en las solicitudes
|
|
82
|
-
app.use(queueSystem.queueMiddleware());
|
|
83
|
-
|
|
84
|
-
// Aplicar el router a la aplicación
|
|
85
|
-
router.applyToApp(app);
|
|
29
|
+
});
|
|
86
30
|
|
|
87
|
-
//
|
|
88
|
-
|
|
31
|
+
// Cargar rutas desde el directorio
|
|
32
|
+
routeDirectoryLoader.loadRoutesFromDirectory(routerIntegration, './routes')
|
|
33
|
+
.then(routes => {
|
|
34
|
+
console.log(`${routes.length} rutas cargadas desde directorio`);
|
|
35
|
+
// Mostrar las rutas que se han cargado
|
|
36
|
+
routes.forEach(route => {
|
|
37
|
+
console.log(`Ruta cargada: ${route.method} ${route.path}`);
|
|
38
|
+
});
|
|
39
|
+
})
|
|
40
|
+
.catch(error => {
|
|
41
|
+
console.error('Error cargando rutas desde directorio:', error.message);
|
|
42
|
+
});
|
|
89
43
|
|
|
90
|
-
//
|
|
91
|
-
|
|
44
|
+
// Aplicar el router a la aplicación Express
|
|
45
|
+
routerIntegration.applyToApp(app);
|
|
92
46
|
|
|
93
|
-
// Middleware para
|
|
94
|
-
app.use((
|
|
95
|
-
|
|
96
|
-
res.status(500).json({
|
|
97
|
-
error: 'Ocurrió un error en el servidor',
|
|
98
|
-
message: err.message
|
|
99
|
-
});
|
|
47
|
+
// Middleware para manejar errores 404
|
|
48
|
+
app.use((req, res) => {
|
|
49
|
+
res.status(404).json({ error: 'Ruta no encontrada' });
|
|
100
50
|
});
|
|
101
51
|
|
|
102
52
|
// Iniciar el servidor
|
|
103
|
-
app.listen(
|
|
104
|
-
console.log(`Servidor corriendo en
|
|
105
|
-
console.log('
|
|
106
|
-
console.log('-
|
|
107
|
-
console.log('-
|
|
108
|
-
console.log('-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log(`${route.method} ${route.path} -> ${route.controller}.${route.action}`);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
53
|
+
app.listen(PORT, () => {
|
|
54
|
+
console.log(`Servidor corriendo en el puerto ${PORT}`);
|
|
55
|
+
console.log('Endpoints disponibles:');
|
|
56
|
+
console.log('- GET /');
|
|
57
|
+
console.log('- GET /init');
|
|
58
|
+
console.log('- GET /users/:id');
|
|
59
|
+
console.log('- GET /products/:category/:id');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
module.exports = app;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Changelog Detallado: Modificaciones al Core de Express-Router-BDS
|
|
2
|
+
|
|
3
|
+
## Archivo: `/node_modules/express-router-bds/ExpressRouter.js`
|
|
4
|
+
|
|
5
|
+
### Cambio 1: Llamada al hook `before_route_processing`
|
|
6
|
+
- **Fecha**: 15 de febrero de 2026
|
|
7
|
+
- **Línea(s)**: 154
|
|
8
|
+
- **Antes**:
|
|
9
|
+
```javascript
|
|
10
|
+
this.hooks.doAction('before_route_processing', req, res, next);
|
|
11
|
+
```
|
|
12
|
+
- **Después**:
|
|
13
|
+
```javascript
|
|
14
|
+
this.hooks.doAction('before_route_processing', req, res);
|
|
15
|
+
```
|
|
16
|
+
- **Motivo**: Evitar que el hook reciba el parámetro `next` para prevenir conflictos con el flujo normal de Express
|
|
17
|
+
|
|
18
|
+
### Cambio 2: Manejo de headers en ejecución del handler
|
|
19
|
+
- **Fecha**: 15 de febrero de 2026
|
|
20
|
+
- **Líneas**: 185-187
|
|
21
|
+
- **Antes**:
|
|
22
|
+
```javascript
|
|
23
|
+
if (!res.headersSent) {
|
|
24
|
+
matchedRoute.route.handler(req, res, next);
|
|
25
|
+
} else {
|
|
26
|
+
console.warn('Headers ya fueron enviados, no se puede ejecutar el handler');
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
- **Después**:
|
|
30
|
+
```javascript
|
|
31
|
+
if (!res.headersSent) {
|
|
32
|
+
// Añadir logging para depuración
|
|
33
|
+
console.log(`Ejecutando handler para ruta: ${req.method} ${req.url}`);
|
|
34
|
+
matchedRoute.route.handler(req, res, next);
|
|
35
|
+
} else {
|
|
36
|
+
console.warn('Headers ya fueron enviados, no se puede ejecutar el handler');
|
|
37
|
+
next();
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
- **Motivo**: Añadir logging para depuración y asegurar que se llame a `next()` si los headers ya han sido enviados
|
|
41
|
+
|
|
42
|
+
### Cambio 3: Llamada a hooks de handler
|
|
43
|
+
- **Fecha**: 15 de febrero de 2026
|
|
44
|
+
- **Línea(s)**: 180
|
|
45
|
+
- **Antes**:
|
|
46
|
+
```javascript
|
|
47
|
+
this.hooks.doAction('before_route_handler', req, res, next);
|
|
48
|
+
```
|
|
49
|
+
- **Después**:
|
|
50
|
+
```javascript
|
|
51
|
+
this.hooks.doAction('before_route_handler', req, res);
|
|
52
|
+
```
|
|
53
|
+
- **Motivo**: Consistencia con el manejo de hooks para evitar conflictos con `next()`
|
|
54
|
+
|
|
55
|
+
### Cambio 4: Manejo de errores en handler
|
|
56
|
+
- **Fecha**: 15 de febrero de 2026
|
|
57
|
+
- **Línea(s)**: 193
|
|
58
|
+
- **Antes**:
|
|
59
|
+
```javascript
|
|
60
|
+
this.hooks.doAction('route_handler_error', error, req, res, next);
|
|
61
|
+
```
|
|
62
|
+
- **Después**:
|
|
63
|
+
```javascript
|
|
64
|
+
this.hooks.doAction('route_handler_error', error, req, res);
|
|
65
|
+
```
|
|
66
|
+
- **Motivo**: Consistencia con el manejo de hooks para evitar conflictos con `next()`
|
|
67
|
+
|
|
68
|
+
## Archivo: `/node_modules/express-router-bds/RouteLoader.js`
|
|
69
|
+
|
|
70
|
+
### Cambio 1: Manejo seguro de headers en handlers con contentType
|
|
71
|
+
- **Fecha**: 15 de febrero de 2026
|
|
72
|
+
- **Líneas**: 178-195
|
|
73
|
+
- **Antes**:
|
|
74
|
+
```javascript
|
|
75
|
+
// Ejecutar el handler original
|
|
76
|
+
try {
|
|
77
|
+
if (handler.constructor.name === 'AsyncFunction') {
|
|
78
|
+
await handler(req, res, next);
|
|
79
|
+
} else {
|
|
80
|
+
handler(req, res, next);
|
|
81
|
+
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// Si ocurre un error, asegurarse de no intentar enviar headers otra vez
|
|
84
|
+
if (!res.headersSent) {
|
|
85
|
+
next(error);
|
|
86
|
+
} else {
|
|
87
|
+
console.error('Error en el handler:', error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
- **Después**:
|
|
92
|
+
```javascript
|
|
93
|
+
// Ejecutar el handler original solo si no se han enviado headers aún
|
|
94
|
+
if (!res.headersSent) {
|
|
95
|
+
try {
|
|
96
|
+
if (handler.constructor.name === 'AsyncFunction') {
|
|
97
|
+
await handler(req, res, next);
|
|
98
|
+
} else {
|
|
99
|
+
handler(req, res, next);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Si ocurre un error, asegurarse de no intentar enviar headers otra vez
|
|
103
|
+
if (!res.headersSent) {
|
|
104
|
+
next(error);
|
|
105
|
+
} else {
|
|
106
|
+
console.error('Error en el handler:', error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
- **Motivo**: Asegurar que los handlers no se ejecuten si los headers ya han sido enviados
|
|
112
|
+
|
|
113
|
+
### Cambio 2: Eliminación de wrapper adicional
|
|
114
|
+
- **Fecha**: 15 de febrero de 2026
|
|
115
|
+
- **Líneas**: 197-217
|
|
116
|
+
- **Antes**:
|
|
117
|
+
```javascript
|
|
118
|
+
// Crear un handler que envuelva completamente la lógica para prevenir doble ejecución
|
|
119
|
+
const wrappedHandler = async (req, res, next) => {
|
|
120
|
+
// Verificar que no se hayan enviado headers aún
|
|
121
|
+
if (res.headersSent) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Ejecutar el handler final
|
|
126
|
+
try {
|
|
127
|
+
await finalHandler(req, res, next);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (!res.headersSent) {
|
|
130
|
+
next(error);
|
|
131
|
+
} else {
|
|
132
|
+
console.error('Error en el handler:', error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Agregar la ruta con el handler envuelto
|
|
138
|
+
routerIntegration.addRoute(route.method, route.path, wrappedHandler);
|
|
139
|
+
```
|
|
140
|
+
- **Después**:
|
|
141
|
+
```javascript
|
|
142
|
+
// Agregar la ruta con el handler final (sin doble wrapper)
|
|
143
|
+
// Asegurarse de que el handler es correcto
|
|
144
|
+
routerIntegration.addRoute(route.method, route.path, finalHandler);
|
|
145
|
+
```
|
|
146
|
+
- **Motivo**: Eliminar wrapper redundante que podría causar problemas de ejecución doble
|
|
147
|
+
|
|
148
|
+
## Archivo: `/node_modules/express-router-bds/RouteMatcher.js`
|
|
149
|
+
|
|
150
|
+
### Cambio 1: Mejora en la conversión de rutas a expresiones regulares
|
|
151
|
+
- **Fecha**: 15 de febrero de 2026
|
|
152
|
+
- **Líneas**: 213-225
|
|
153
|
+
- **Antes**:
|
|
154
|
+
```javascript
|
|
155
|
+
// Lógica de escape de caracteres especiales
|
|
156
|
+
let escapedPath = '';
|
|
157
|
+
for (let i = 0; i < path.length; i++) {
|
|
158
|
+
const char = path[i];
|
|
159
|
+
if (char.match(/[.+?^${}()|[\]\\-]/) && !(i > 0 && path[i-1] === ':')) {
|
|
160
|
+
escapedPath += '\\' + char;
|
|
161
|
+
} else {
|
|
162
|
+
escapedPath += char;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
- **Después**: (Sin cambios, ya que no se modificó este archivo directamente)
|
|
167
|
+
|
|
168
|
+
## Resumen de Impacto
|
|
169
|
+
|
|
170
|
+
Los cambios realizados al core de `express-router-bds` se enfocaron principalmente en:
|
|
171
|
+
|
|
172
|
+
1. **Evitar conflictos con el parámetro `next`** en las llamadas a hooks
|
|
173
|
+
2. **Mejorar la seguridad en el manejo de headers** para prevenir el error `ERR_HTTP_HEADERS_SENT`
|
|
174
|
+
3. **Eliminar wrappers redundantes** que podían causar ejecuciones dobles de handlers
|
|
175
|
+
4. **Agregar verificaciones adicionales** para asegurar que los handlers solo se ejecuten cuando sea seguro hacerlo
|
|
176
|
+
|
|
177
|
+
Estos cambios resolvieron el problema fundamental donde las rutas se identificaban pero los handlers no se ejecutaban, permitiendo que la aplicación funcione correctamente.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// controllers/mainController.js
|
|
2
|
+
// Controlador principal de la aplicación
|
|
3
|
+
|
|
4
|
+
class MainController {
|
|
5
|
+
/**
|
|
6
|
+
* Maneja la ruta raíz
|
|
7
|
+
* @param {Object} req - Objeto de solicitud
|
|
8
|
+
* @param {Object} res - Objeto de respuesta
|
|
9
|
+
*/
|
|
10
|
+
home(req, res) {
|
|
11
|
+
if (!res.headersSent) {
|
|
12
|
+
res.json({
|
|
13
|
+
message: '¡Bienvenido a la aplicación Express con Router-BDS!',
|
|
14
|
+
timestamp: new Date().toISOString()
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
console.log('Headers ya fueron enviados en home handler');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Maneja la ruta de inicialización
|
|
23
|
+
* @param {Object} req - Objeto de solicitud
|
|
24
|
+
* @param {Object} res - Objeto de respuesta
|
|
25
|
+
*/
|
|
26
|
+
init(req, res) {
|
|
27
|
+
res.json({
|
|
28
|
+
message: 'Aplicación inicializada correctamente con Router-BDS',
|
|
29
|
+
status: 'success'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = new MainController();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// controllers/productController.js
|
|
2
|
+
// Controlador para gestionar productos
|
|
3
|
+
|
|
4
|
+
class ProductController {
|
|
5
|
+
/**
|
|
6
|
+
* Obtiene un producto por categoría e ID
|
|
7
|
+
* @param {Object} req - Objeto de solicitud
|
|
8
|
+
* @param {Object} res - Objeto de respuesta
|
|
9
|
+
*/
|
|
10
|
+
getProduct(req, res) {
|
|
11
|
+
const category = req.params.category;
|
|
12
|
+
const productId = req.params.id;
|
|
13
|
+
res.json({
|
|
14
|
+
message: `Obteniendo producto de la categoría "${category}" con ID: ${productId}`,
|
|
15
|
+
category: category,
|
|
16
|
+
productId: productId,
|
|
17
|
+
timestamp: new Date().toISOString()
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = new ProductController();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// controllers/userController.js
|
|
2
|
+
// Controlador para gestionar usuarios
|
|
3
|
+
|
|
4
|
+
class UserController {
|
|
5
|
+
/**
|
|
6
|
+
* Obtiene un usuario por ID
|
|
7
|
+
* @param {Object} req - Objeto de solicitud
|
|
8
|
+
* @param {Object} res - Objeto de respuesta
|
|
9
|
+
*/
|
|
10
|
+
getUser(req, res) {
|
|
11
|
+
const userId = req.params.id;
|
|
12
|
+
res.json({
|
|
13
|
+
message: `Obteniendo información del usuario con ID: ${userId}`,
|
|
14
|
+
userId: userId,
|
|
15
|
+
timestamp: new Date().toISOString()
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = new UserController();
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Documento de Análisis: Bug de Enrutamiento en Express-Router-BDS
|
|
2
|
+
|
|
3
|
+
## Descripción del Problema
|
|
4
|
+
|
|
5
|
+
La aplicación Express con el sistema de enrutamiento personalizado `express-router-bds` presentaba un fallo crítico donde:
|
|
6
|
+
|
|
7
|
+
- Las rutas se identificaban correctamente en el sistema
|
|
8
|
+
- El log mostraba mensajes como "Ruta encontrada: /"
|
|
9
|
+
- Sin embargo, los handlers correspondientes no se ejecutaban
|
|
10
|
+
- El sistema respondía con "Ruta no encontrada" para todas las rutas válidas
|
|
11
|
+
- Aparecía el error `ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client`
|
|
12
|
+
|
|
13
|
+
## Análisis Técnico
|
|
14
|
+
|
|
15
|
+
### Componentes Involucrados
|
|
16
|
+
|
|
17
|
+
1. **ExpressRouter.js** - Middleware principal de enrutamiento
|
|
18
|
+
2. **hooks-config.js** - Configuración de hooks personalizados
|
|
19
|
+
3. **RouteMatcher.js** - Sistema de coincidencia de rutas
|
|
20
|
+
4. **RouteLoader.js** - Cargador de rutas desde archivos JSON
|
|
21
|
+
|
|
22
|
+
### Causa Raíz
|
|
23
|
+
|
|
24
|
+
El problema se originó por una **interacción incorrecta entre el sistema de hooks y el flujo normal de Express**:
|
|
25
|
+
|
|
26
|
+
1. El hook `before_route_processing` se definía con 3 parámetros: `(req, res, next)`
|
|
27
|
+
2. Este hook se registraba en `hooks-config.js` y se llamaba en `ExpressRouter.js`
|
|
28
|
+
3. El hook contenía una llamada a `next()`, lo que causaba que `next()` se ejecutara dos veces:
|
|
29
|
+
- Una vez por el hook
|
|
30
|
+
- Otra vez por el flujo normal del middleware
|
|
31
|
+
4. Esto provocaba que los headers se enviaran antes de tiempo
|
|
32
|
+
5. Cuando el sistema intentaba ejecutar el handler real, fallaba porque los headers ya habían sido enviados
|
|
33
|
+
|
|
34
|
+
### Flujo de Ejecución Defectuoso
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// En ExpressRouter.js
|
|
38
|
+
this.hooks.doAction('before_route_processing', req, res, next);
|
|
39
|
+
|
|
40
|
+
// En hooks-config.js
|
|
41
|
+
this.hooks.addAction('before_route_processing', (req, res, next) => {
|
|
42
|
+
console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
|
|
43
|
+
next(); // ← ESTO CAUSABA EL PROBLEMA
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Solución Implementada
|
|
48
|
+
|
|
49
|
+
### Cambios Realizados
|
|
50
|
+
|
|
51
|
+
#### 1. En `ExpressRouter.js`
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
// ANTES
|
|
55
|
+
this.hooks.doAction('before_route_processing', req, res, next);
|
|
56
|
+
|
|
57
|
+
// DESPUÉS
|
|
58
|
+
this.hooks.doAction('before_route_processing', req, res);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### 2. En `hooks-config.js`
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// ANTES
|
|
65
|
+
this.hooks.addAction('before_route_processing', (req, res, next) => {
|
|
66
|
+
console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
|
|
67
|
+
next(); // ← ESTA LLAMADA CAUSABA EL CONFLICTO
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// DESPUÉS
|
|
71
|
+
this.hooks.addAction('before_route_processing', (req, res, next) => {
|
|
72
|
+
console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
|
|
73
|
+
// next(); ← COMENTADO PARA EVITAR DUPLICACIÓN
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### 3. En `mainController.js` (como medida preventiva)
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
home(req, res) {
|
|
81
|
+
if (!res.headersSent) {
|
|
82
|
+
res.json({
|
|
83
|
+
message: '¡Bienvenido a la aplicación Express con Router-BDS!',
|
|
84
|
+
timestamp: new Date().toISOString()
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
console.log('Headers ya fueron enviados en home handler');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Resultado
|
|
93
|
+
|
|
94
|
+
Después de aplicar la solución:
|
|
95
|
+
|
|
96
|
+
✅ **Todas las rutas estáticas funcionan** (`/`, `/init`)
|
|
97
|
+
✅ **Todas las rutas parametrizadas funcionan** (`/users/:id`, `/products/:category/:id`)
|
|
98
|
+
✅ **No hay más errores de headers duplicados**
|
|
99
|
+
✅ **Los parámetros de ruta se pasan correctamente**
|
|
100
|
+
✅ **El sistema de hooks sigue funcionando para logging**
|
|
101
|
+
|
|
102
|
+
## Lecciones Aprendidas
|
|
103
|
+
|
|
104
|
+
1. **Separación de responsabilidades**: Los hooks deben usarse para extensibilidad, no para controlar el flujo principal
|
|
105
|
+
2. **Cuidado con `next()`**: Llamar a `next()` en múltiples lugares puede causar efectos colaterales
|
|
106
|
+
3. **Importancia de pruebas**: Este tipo de error puede pasar desapercibido sin pruebas adecuadas
|
|
107
|
+
4. **Logging vs. Control de Flujo**: Los hooks de logging no deben alterar el flujo de la aplicación
|
|
108
|
+
|
|
109
|
+
## Recomendaciones
|
|
110
|
+
|
|
111
|
+
- Implementar pruebas unitarias para el sistema de enrutamiento
|
|
112
|
+
- Documentar claramente la responsabilidad de cada hook
|
|
113
|
+
- Considerar el uso de un sistema de middleware más explícito para evitar confusiones
|
|
114
|
+
- Añadir validaciones para prevenir llamadas duplicadas a `next()`
|
package/hooks-config.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// hooks-config.js
|
|
2
|
+
// Archivo de configuración para los hooks y filtros de la aplicación
|
|
3
|
+
|
|
4
|
+
class AppHooks {
|
|
5
|
+
constructor(expressApp, hookedInstance) {
|
|
6
|
+
this.app = expressApp;
|
|
7
|
+
this.hooks = hookedInstance;
|
|
8
|
+
|
|
9
|
+
this.initHooks();
|
|
10
|
+
this.initFilters();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
initHooks() {
|
|
14
|
+
// Hook para antes de procesar cualquier ruta - SOLO PARA LOGGING
|
|
15
|
+
this.hooks.addAction('before_route_processing', (req, res, next) => {
|
|
16
|
+
console.log(`[HOOK-ACTIVADO] Procesando ruta: ${req.method} ${req.url}`);
|
|
17
|
+
// IMPORTANTE: No llamar a next() aquí, eso es responsabilidad del middleware
|
|
18
|
+
// next(); // Comentado para evitar la duplicación de next()
|
|
19
|
+
}, 10, 3); // Prioridad 10 y acepta 3 argumentos
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
initFilters() {
|
|
23
|
+
// Filtro para modificar el mensaje de bienvenida
|
|
24
|
+
this.hooks.addFilter('welcome_message', (message, req) => {
|
|
25
|
+
// Agregar información del usuario o fecha al mensaje
|
|
26
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
27
|
+
return `${message} - Petición recibida a las ${timestamp}`;
|
|
28
|
+
}, 10, 2); // Prioridad 10 y acepta 2 argumentos
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Método para aplicar un filtro específico
|
|
32
|
+
applyWelcomeMessageFilter(message, req) {
|
|
33
|
+
return this.hooks.applyFilters('welcome_message', message, req);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Método para ejecutar un hook específico
|
|
37
|
+
executeRootBeforeHook(req, res, next) {
|
|
38
|
+
this.hooks.doAction('root:before', req, res, next);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = AppHooks;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// hooks-personalizados.js
|
|
2
|
+
// Archivo de hooks personalizados para la aplicación
|
|
3
|
+
|
|
4
|
+
class HooksPersonalizados {
|
|
5
|
+
constructor(routerIntegration) {
|
|
6
|
+
this.routerIntegration = routerIntegration;
|
|
7
|
+
this.initHooks();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
initHooks() {
|
|
11
|
+
// Hook que se ejecuta antes de cargar rutas desde un archivo
|
|
12
|
+
this.routerIntegration.addHook('pre_route_load', (filePath, routerIntegration) => {
|
|
13
|
+
console.log(`[PRE_ROUTE_LOAD] Iniciando carga de rutas desde: ${filePath}`);
|
|
14
|
+
}, 10, 2);
|
|
15
|
+
|
|
16
|
+
// Hook que se ejecuta después de cargar rutas desde un archivo
|
|
17
|
+
this.routerIntegration.addHook('post_route_load', (routes, routerIntegration) => {
|
|
18
|
+
console.log(`[POST_ROUTE_LOAD] Se cargaron ${routes.length} rutas exitosamente`);
|
|
19
|
+
routes.forEach((route, index) => {
|
|
20
|
+
console.log(`[POST_ROUTE_LOAD] Ruta ${index + 1}: ${route.method} ${route.path}`);
|
|
21
|
+
});
|
|
22
|
+
}, 10, 2);
|
|
23
|
+
|
|
24
|
+
// Hook que se ejecuta antes de procesar cualquier ruta
|
|
25
|
+
this.routerIntegration.addHook('before_route_processing', (req, res) => {
|
|
26
|
+
console.log(`[BEFORE_ROUTE_PROCESSING] Procesando ruta: ${req.method} ${req.url}`);
|
|
27
|
+
}, 10, 2);
|
|
28
|
+
|
|
29
|
+
// Hook que se ejecuta cuando se encuentra una ruta coincidente
|
|
30
|
+
this.routerIntegration.addHook('route_matched', (matchedRoute, req, res) => {
|
|
31
|
+
console.log(`[ROUTE_MATCHED] Ruta encontrada: ${matchedRoute.route.path}`);
|
|
32
|
+
if (matchedRoute.params && Object.keys(matchedRoute.params).length > 0) {
|
|
33
|
+
console.log(`[ROUTE_MATCHED] Parámetros:`, matchedRoute.params);
|
|
34
|
+
}
|
|
35
|
+
}, 10, 3);
|
|
36
|
+
|
|
37
|
+
// Hook que se ejecuta antes de ejecutar un handler de ruta
|
|
38
|
+
this.routerIntegration.addHook('before_route_handler', (req, res) => {
|
|
39
|
+
console.log(`[BEFORE_ROUTE_HANDLER] Antes de ejecutar handler para: ${req.method} ${req.url}`);
|
|
40
|
+
}, 10, 2);
|
|
41
|
+
|
|
42
|
+
// Hook que se ejecuta después de ejecutar un handler de ruta
|
|
43
|
+
this.routerIntegration.addHook('after_route_handler', (req, res) => {
|
|
44
|
+
console.log(`[AFTER_ROUTE_HANDLER] Después de ejecutar handler para: ${req.method} ${req.url}`);
|
|
45
|
+
}, 10, 2);
|
|
46
|
+
|
|
47
|
+
// Hook que se ejecuta cuando ocurre un error en un handler de ruta
|
|
48
|
+
this.routerIntegration.addHook('route_handler_error', (error, req, res) => {
|
|
49
|
+
console.log(`[ROUTE_HANDLER_ERROR] Error en handler para: ${req.method} ${req.url}`, error.message);
|
|
50
|
+
}, 10, 3);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = HooksPersonalizados;
|
package/package.json
CHANGED
|
@@ -1,58 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expressed-example-app",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node app.js",
|
|
8
|
-
"
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
9
|
},
|
|
10
|
+
"keywords": ["web express example"],
|
|
11
|
+
"author": "BDS",
|
|
12
|
+
"license": "ISC",
|
|
10
13
|
"dependencies": {
|
|
11
14
|
"express": "^4.22.1",
|
|
12
|
-
"express-hooked": "^1.0.
|
|
15
|
+
"express-hooked": "^1.0.2",
|
|
13
16
|
"express-queued": "^1.0.0",
|
|
14
|
-
"express-router-bds": "^1.
|
|
15
|
-
}
|
|
16
|
-
"keywords": [
|
|
17
|
-
"express",
|
|
18
|
-
"hooks",
|
|
19
|
-
"middleware",
|
|
20
|
-
"router",
|
|
21
|
-
"routing",
|
|
22
|
-
"queues",
|
|
23
|
-
"queue-system",
|
|
24
|
-
"hook-system",
|
|
25
|
-
"event-driven",
|
|
26
|
-
"plugin-architecture",
|
|
27
|
-
"modular",
|
|
28
|
-
"scalable",
|
|
29
|
-
"example",
|
|
30
|
-
"demo",
|
|
31
|
-
"boilerplate",
|
|
32
|
-
"framework",
|
|
33
|
-
"web-framework",
|
|
34
|
-
"api",
|
|
35
|
-
"rest-api",
|
|
36
|
-
"microservices",
|
|
37
|
-
"async",
|
|
38
|
-
"asynchronous",
|
|
39
|
-
"background-jobs",
|
|
40
|
-
"task-queue",
|
|
41
|
-
"express-plugins",
|
|
42
|
-
"express-middleware",
|
|
43
|
-
"nodejs",
|
|
44
|
-
"javascript",
|
|
45
|
-
"backend",
|
|
46
|
-
"server"
|
|
47
|
-
],
|
|
48
|
-
"author": "Benjamin Sanchez Cardenas",
|
|
49
|
-
"license": "Apache-2.0",
|
|
50
|
-
"repository": {
|
|
51
|
-
"type": "git",
|
|
52
|
-
"url": "https://gitlab.com/bytedogssyndicate1/expressed-example-app.git"
|
|
53
|
-
},
|
|
54
|
-
"bugs": {
|
|
55
|
-
"url": "https://gitlab.com/bytedogssyndicate1/expressed-example-app/issues"
|
|
56
|
-
},
|
|
57
|
-
"homepage": "https://gitlab.com/bytedogssyndicate1/expressed-example-app#readme"
|
|
17
|
+
"express-router-bds": "^1.1.1"
|
|
18
|
+
}
|
|
58
19
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"method": "GET",
|
|
4
|
+
"path": "/",
|
|
5
|
+
"controller": "./controllers/mainController",
|
|
6
|
+
"handler": "home",
|
|
7
|
+
"contentType": "application/json"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"method": "GET",
|
|
11
|
+
"path": "/init",
|
|
12
|
+
"controller": "./controllers/mainController",
|
|
13
|
+
"handler": "init",
|
|
14
|
+
"contentType": "application/json"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"method": "GET",
|
|
18
|
+
"path": "/users/:id",
|
|
19
|
+
"controller": "./controllers/userController",
|
|
20
|
+
"handler": "getUser",
|
|
21
|
+
"contentType": "application/json"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"method": "GET",
|
|
25
|
+
"path": "/products/:category/:id",
|
|
26
|
+
"controller": "./controllers/productController",
|
|
27
|
+
"handler": "getProduct",
|
|
28
|
+
"contentType": "application/json"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"method": "GET",
|
|
32
|
+
"path": "/test-hook",
|
|
33
|
+
"controller": "./controllers/mainController",
|
|
34
|
+
"handler": "home",
|
|
35
|
+
"contentType": "application/json"
|
|
36
|
+
}
|
|
37
|
+
]
|
package/test-hooks.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// test-hooks.js
|
|
2
|
+
// Script para probar los hooks pre_route_load y post_route_load
|
|
3
|
+
|
|
4
|
+
const ExpressRouterIntegration = require('express-router-bds');
|
|
5
|
+
const RouteLoader = require('express-router-bds/RouteLoader');
|
|
6
|
+
const HooksPersonalizados = require('./hooks-personalizados');
|
|
7
|
+
|
|
8
|
+
async function testHooks() {
|
|
9
|
+
console.log('Iniciando prueba de hooks pre_route_load y post_route_load...');
|
|
10
|
+
|
|
11
|
+
// Crear instancia del router
|
|
12
|
+
const routerIntegration = new ExpressRouterIntegration();
|
|
13
|
+
|
|
14
|
+
// Inicializar hooks personalizados
|
|
15
|
+
const hooksPersonalizados = new HooksPersonalizados(routerIntegration);
|
|
16
|
+
|
|
17
|
+
// Crear instancia de RouteLoader
|
|
18
|
+
const routeLoader = new RouteLoader();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Cargar rutas desde el archivo de prueba
|
|
22
|
+
console.log('\nCargando rutas desde test-routes.json...\n');
|
|
23
|
+
const routes = await routeLoader.loadRoutes(routerIntegration, './test-routes.json');
|
|
24
|
+
|
|
25
|
+
console.log(`\nResultado: ${routes.length} rutas cargadas exitosamente`);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error al cargar rutas:', error.message);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
testHooks();
|
package/test-routes.json
ADDED
package/config/routes.json
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"routes": [
|
|
3
|
-
{
|
|
4
|
-
"method": "GET",
|
|
5
|
-
"path": "/",
|
|
6
|
-
"controller": "HomeController.index",
|
|
7
|
-
"action": "index"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"method": "GET",
|
|
11
|
-
"path": "/users",
|
|
12
|
-
"controller": "UserController.list",
|
|
13
|
-
"action": "list"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"method": "GET",
|
|
17
|
-
"path": "/users/:id",
|
|
18
|
-
"controller": "UserController.show",
|
|
19
|
-
"action": "show"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"method": "POST",
|
|
23
|
-
"path": "/users",
|
|
24
|
-
"controller": "UserController.create",
|
|
25
|
-
"action": "create"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"method": "PUT",
|
|
29
|
-
"path": "/users/:id",
|
|
30
|
-
"controller": "UserController.update",
|
|
31
|
-
"action": "update"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"method": "DELETE",
|
|
35
|
-
"path": "/users/:id",
|
|
36
|
-
"controller": "UserController.destroy",
|
|
37
|
-
"action": "destroy"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"method": "GET",
|
|
41
|
-
"path": "/products",
|
|
42
|
-
"controller": "ProductController.list",
|
|
43
|
-
"action": "list"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"method": "GET",
|
|
47
|
-
"path": "/products/:id",
|
|
48
|
-
"controller": "ProductController.show",
|
|
49
|
-
"action": "show"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"method": "POST",
|
|
53
|
-
"path": "/products",
|
|
54
|
-
"controller": "ProductController.create",
|
|
55
|
-
"action": "create"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"method": "GET",
|
|
59
|
-
"path": "/queue/status",
|
|
60
|
-
"controller": "QueueController.status",
|
|
61
|
-
"action": "status"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"method": "GET",
|
|
65
|
-
"path": "/endpoints",
|
|
66
|
-
"controller": "EndpointController.listEndpoints",
|
|
67
|
-
"action": "listEndpoints"
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controlador de endpoints
|
|
3
|
-
*/
|
|
4
|
-
class EndpointController {
|
|
5
|
-
/**
|
|
6
|
-
* Lista todos los endpoints disponibles
|
|
7
|
-
* @param {Object} req - Objeto de solicitud
|
|
8
|
-
* @param {Object} res - Objeto de respuesta
|
|
9
|
-
*/
|
|
10
|
-
static listEndpoints(req, res) {
|
|
11
|
-
// El sistema de hooks será utilizado para obtener la lista de endpoints
|
|
12
|
-
if (req.app.locals.hooks) {
|
|
13
|
-
// Simular la recopilación de endpoints desde el sistema de rutas
|
|
14
|
-
const routes = [
|
|
15
|
-
{ method: 'GET', path: '/', description: 'Inicio de la aplicación' },
|
|
16
|
-
{ method: 'GET', path: '/users', description: 'Lista todos los usuarios' },
|
|
17
|
-
{ method: 'GET', path: '/users/:id', description: 'Muestra un usuario específico' },
|
|
18
|
-
{ method: 'POST', path: '/users', description: 'Crea un nuevo usuario' },
|
|
19
|
-
{ method: 'PUT', path: '/users/:id', description: 'Actualiza un usuario' },
|
|
20
|
-
{ method: 'DELETE', path: '/users/:id', description: 'Elimina un usuario' },
|
|
21
|
-
{ method: 'GET', path: '/products', description: 'Lista todos los productos' },
|
|
22
|
-
{ method: 'GET', path: '/products/:id', description: 'Muestra un producto específico' },
|
|
23
|
-
{ method: 'POST', path: '/products', description: 'Crea un nuevo producto' },
|
|
24
|
-
{ method: 'GET', path: '/queue/status', description: 'Estado del sistema de colas' },
|
|
25
|
-
{ method: 'GET', path: '/endpoints', description: 'Lista todos los endpoints disponibles' }
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// Permitir que los hooks modifiquen la lista de endpoints
|
|
29
|
-
const processedEndpoints = req.app.locals.hooks.applyFilters('endpoints_list', routes);
|
|
30
|
-
|
|
31
|
-
res.json({
|
|
32
|
-
message: 'Lista de endpoints disponibles',
|
|
33
|
-
endpoints: processedEndpoints
|
|
34
|
-
});
|
|
35
|
-
} else {
|
|
36
|
-
res.status(500).json({
|
|
37
|
-
error: 'Sistema de hooks no disponible'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = EndpointController;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controlador de inicio
|
|
3
|
-
*/
|
|
4
|
-
class HomeController {
|
|
5
|
-
/**
|
|
6
|
-
* Acción de inicio
|
|
7
|
-
* @param {Object} req - Objeto de solicitud
|
|
8
|
-
* @param {Object} res - Objeto de respuesta
|
|
9
|
-
*/
|
|
10
|
-
static index(req, res) {
|
|
11
|
-
res.json({
|
|
12
|
-
message: 'Bienvenido a la aplicación de ejemplo',
|
|
13
|
-
version: '1.0.0',
|
|
14
|
-
timestamp: new Date().toISOString()
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = HomeController;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controlador de productos
|
|
3
|
-
*/
|
|
4
|
-
class ProductController {
|
|
5
|
-
/**
|
|
6
|
-
* Lista todos los productos
|
|
7
|
-
* @param {Object} req - Objeto de solicitud
|
|
8
|
-
* @param {Object} res - Objeto de respuesta
|
|
9
|
-
*/
|
|
10
|
-
static list(req, res) {
|
|
11
|
-
res.json({
|
|
12
|
-
message: 'Lista de productos',
|
|
13
|
-
products: [
|
|
14
|
-
{ id: 1, name: 'Laptop', price: 999.99 },
|
|
15
|
-
{ id: 2, name: 'Mouse', price: 29.99 }
|
|
16
|
-
]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Muestra un producto específico
|
|
22
|
-
* @param {Object} req - Objeto de solicitud
|
|
23
|
-
* @param {Object} res - Objeto de respuesta
|
|
24
|
-
*/
|
|
25
|
-
static show(req, res) {
|
|
26
|
-
const productId = req.params.id;
|
|
27
|
-
res.json({
|
|
28
|
-
message: `Información del producto ${productId}`,
|
|
29
|
-
product: { id: productId, name: `Product ${productId}`, price: 19.99 }
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Crea un nuevo producto
|
|
35
|
-
* @param {Object} req - Objeto de solicitud
|
|
36
|
-
* @param {Object} res - Objeto de respuesta
|
|
37
|
-
*/
|
|
38
|
-
static create(req, res) {
|
|
39
|
-
const productData = req.body;
|
|
40
|
-
res.status(201).json({
|
|
41
|
-
message: 'Producto creado exitosamente',
|
|
42
|
-
product: { id: Date.now(), ...productData }
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports = ProductController;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controlador del sistema de colas
|
|
3
|
-
*/
|
|
4
|
-
class QueueController {
|
|
5
|
-
/**
|
|
6
|
-
* Obtiene el estado del sistema de colas
|
|
7
|
-
* @param {Object} req - Objeto de solicitud
|
|
8
|
-
* @param {Object} res - Objeto de respuesta
|
|
9
|
-
*/
|
|
10
|
-
static status(req, res) {
|
|
11
|
-
// El sistema de colas será inyectado por el middleware
|
|
12
|
-
if (req.queueSystem) {
|
|
13
|
-
const status = req.queueSystem.getStatus();
|
|
14
|
-
res.json({
|
|
15
|
-
message: 'Estado del sistema de colas',
|
|
16
|
-
status: status
|
|
17
|
-
});
|
|
18
|
-
} else {
|
|
19
|
-
res.status(500).json({
|
|
20
|
-
error: 'Sistema de colas no disponible'
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = QueueController;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Controlador de usuarios
|
|
3
|
-
*/
|
|
4
|
-
class UserController {
|
|
5
|
-
/**
|
|
6
|
-
* Lista todos los usuarios
|
|
7
|
-
* @param {Object} req - Objeto de solicitud
|
|
8
|
-
* @param {Object} res - Objeto de respuesta
|
|
9
|
-
*/
|
|
10
|
-
static list(req, res) {
|
|
11
|
-
res.json({
|
|
12
|
-
message: 'Lista de usuarios',
|
|
13
|
-
users: [
|
|
14
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
15
|
-
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
|
16
|
-
]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Muestra un usuario específico
|
|
22
|
-
* @param {Object} req - Objeto de solicitud
|
|
23
|
-
* @param {Object} res - Objeto de respuesta
|
|
24
|
-
*/
|
|
25
|
-
static show(req, res) {
|
|
26
|
-
const userId = req.params.id;
|
|
27
|
-
res.json({
|
|
28
|
-
message: `Información del usuario ${userId}`,
|
|
29
|
-
user: { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` }
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Crea un nuevo usuario
|
|
35
|
-
* @param {Object} req - Objeto de solicitud
|
|
36
|
-
* @param {Object} res - Objeto de respuesta
|
|
37
|
-
*/
|
|
38
|
-
static create(req, res) {
|
|
39
|
-
const userData = req.body;
|
|
40
|
-
res.status(201).json({
|
|
41
|
-
message: 'Usuario creado exitosamente',
|
|
42
|
-
user: { id: Date.now(), ...userData }
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Actualiza un usuario
|
|
48
|
-
* @param {Object} req - Objeto de solicitud
|
|
49
|
-
* @param {Object} res - Objeto de respuesta
|
|
50
|
-
*/
|
|
51
|
-
static update(req, res) {
|
|
52
|
-
const userId = req.params.id;
|
|
53
|
-
const userData = req.body;
|
|
54
|
-
res.json({
|
|
55
|
-
message: `Usuario ${userId} actualizado`,
|
|
56
|
-
user: { id: userId, ...userData }
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Elimina un usuario
|
|
62
|
-
* @param {Object} req - Objeto de solicitud
|
|
63
|
-
* @param {Object} res - Objeto de respuesta
|
|
64
|
-
*/
|
|
65
|
-
static destroy(req, res) {
|
|
66
|
-
const userId = req.params.id;
|
|
67
|
-
res.json({
|
|
68
|
-
message: `Usuario ${userId} eliminado`
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
module.exports = UserController;
|
package/hooks/AppHooks.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Archivo de hooks para la aplicación
|
|
3
|
-
* Define todos los hooks utilizados en la aplicación
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class AppHooks {
|
|
7
|
-
/**
|
|
8
|
-
* Registra todos los hooks de la aplicación
|
|
9
|
-
* @param {ExpressHooked} hooked - Instancia del sistema de hooks
|
|
10
|
-
*/
|
|
11
|
-
static registerHooks(hooked) {
|
|
12
|
-
// Hook para registrar información cuando comienza una solicitud
|
|
13
|
-
hooked.addAction('request_started', (req, res, next) => {
|
|
14
|
-
console.log(`[${new Date().toISOString()}] Nueva solicitud: ${req.method} ${req.url}`);
|
|
15
|
-
next();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Hook para registrar información cuando se procesa una solicitud
|
|
19
|
-
hooked.addAction('request_processed', (req, res) => {
|
|
20
|
-
console.log(`[${new Date().toISOString()}] Solicitud procesada: ${req.method} ${req.url}`);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Hook para registrar información detallada de cada solicitud
|
|
24
|
-
hooked.addAction('request_details_log', (req, res, routeInfo) => {
|
|
25
|
-
console.log(`\n--- Información de la Solicitud (mediante hook) ---`);
|
|
26
|
-
console.log(`Fecha/Hora: ${new Date().toISOString()}`);
|
|
27
|
-
console.log(`Método HTTP: ${req.method}`);
|
|
28
|
-
console.log(`Ruta: ${req.url}`);
|
|
29
|
-
if (routeInfo) {
|
|
30
|
-
console.log(`Ruta definida: ${routeInfo.method} ${routeInfo.path}`);
|
|
31
|
-
console.log(`Controlador: ${routeInfo.controller}`);
|
|
32
|
-
console.log(`Acción: ${routeInfo.action}`);
|
|
33
|
-
}
|
|
34
|
-
console.log(`IP Cliente: ${req.ip}`);
|
|
35
|
-
console.log(`Headers:`, req.headers);
|
|
36
|
-
if (Object.keys(req.params).length > 0) {
|
|
37
|
-
console.log(`Parámetros:`, req.params);
|
|
38
|
-
}
|
|
39
|
-
if (Object.keys(req.query).length > 0) {
|
|
40
|
-
console.log(`Query params:`, req.query);
|
|
41
|
-
}
|
|
42
|
-
if (req.body && Object.keys(req.body).length > 0) {
|
|
43
|
-
console.log(`Body:`, req.body);
|
|
44
|
-
}
|
|
45
|
-
console.log(`-----------------------------\n`);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Hook para filtrar y modificar la lista de endpoints
|
|
49
|
-
hooked.addFilter('endpoints_list', (endpoints) => {
|
|
50
|
-
// Agregar información adicional a la lista de endpoints
|
|
51
|
-
return endpoints.map(endpoint => ({
|
|
52
|
-
...endpoint,
|
|
53
|
-
addedBy: 'hook-system',
|
|
54
|
-
timestamp: new Date().toISOString()
|
|
55
|
-
}));
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = AppHooks;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Middleware para capturar el body de las solicitudes
|
|
3
|
-
* Permite que el body esté disponible para los hooks
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const express = require('express');
|
|
7
|
-
|
|
8
|
-
// Middleware para capturar el body
|
|
9
|
-
const captureBodyMiddleware = express.json({
|
|
10
|
-
verify: (req, res, buf, encoding) => {
|
|
11
|
-
req.rawBody = buf;
|
|
12
|
-
try {
|
|
13
|
-
req.capturedBody = JSON.parse(buf.toString(encoding));
|
|
14
|
-
} catch (e) {
|
|
15
|
-
req.capturedBody = buf.toString(encoding);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
module.exports = { captureBodyMiddleware };
|