Influencias / usos
-
Otros frameworks similares:
- Zend (PHP)
- Django (Python)
- Sinatra (Ruby)
-
Uso:
- API JSON
- Single Pages
- App tiempo real
Pros
- Rutas
- Parámetros
- Formularios y subida de ficheros
- Cookies
- Sesiones
- Templates
Contras
- Base de datos / ORM
- Autenticación de usuarios
- Seguridad
- Migraciones
- Deployment
- Organización del código
Migraciones
Instalación
-
Instalación Global:
npm install -g express
-
Instalación versiones anteriores:
npm install -g [email protected]
Hola Mundo
var express = require('express');
var app = express();
// C9
var puerto = process.env.PORT || 3000;
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(puerto, function () {
console.log('Example app listening on port ' + puerto);
});
Generador de Express
-
Instalación global del generador
npm install express-generator -g
-
Opciones de instalación
express -h
-
Generar un proyecto
express <nombre_proyecto>
-
Entramos en la carpeta e instalamos las dependencias
cd <nombre_proyecto> && npm install
-
Estructura de un Proyecto (MVC)
├── app.js (Nuestra aplicación - módulo) ├── bin (Gestión de la aplicación) │ └── www ├── package.json (Información y dependencias) ├── public (Nuestros estáticos) │ ├── images │ ├── javascripts │ └── stylesheets │ └── style.css ├── routes (Nuestros controladores) │ ├── index.js │ └── users.js └── views (Nuestras vistas/plantillas) ├── error.jade ├── index.jade └── layout.jade
-
Ejecutando la aplicación:
- Windows
set DEBUG=<nombre_proyecto>:* & npm start
- MacOS/Linux
DEBUG=<nombre_proyecto>:* npm start
- Windows
Partes Claves
Mecánica: app.set()
-
Nos permite establecer la configuración de Express
-
Podemos almacenar datos personalizados de manera global
-
Ejemplos
- Guardando la versión
app.set('version', '1.5.0'); app.get('version'); // 1.5.0
- Maneras de Habilitar contenido
app.enable('dia_soleado'); // igual a -> app.set('dia_soleado', true); app.enabled('dia_soleado'); // igual a -> app.get('dia_soleado') === true; app.disabled('dia_soleado'); // igual a -> app.get('dia_soleado') === false;
- Definiendo el puerto
app.set('port', process.env.PORT || 3000);
- Configuraciones según el entorno
- Para todos los entornos
NODE_ENV=production node app.js
app.configure(function(){ app.set('estado_aplicacion', '*'); });
- Solo desarrollo
NODE_ENV=development node app.js
app.configure('development', function(){ app.set('estado_aplicacion', 'development'); });
- Solo producción
NODE_ENV=production node app.js
app.configure('production', function(){ app.set('estado_aplicacion', 'production'); });
- Solo personalizado
NODE_ENV=personalizado1 node app.js
app.configure('personalizado1', function(){ app.set('estado_aplicacion', 'personalizado1'); });
- Motores de Plantillas
- Variables locales (solo disponibles para las plantillas)
// Guardando app.locals.title = 'My App'; app.locals.email = '[email protected]'; // Usando app.locals.title // My App app.locals.email // [email protected]
- Definiendo el sistema de plantillas que usaremos
// npm install jade --save var express = require('express'); var jade = require('jade'); var app = express(); app.set('view engine', 'jade'); app.get('/', function (req, res) { res.render('index', { title: 'Hey', message: 'Hello there!'}); });
Mecánica: app.all(), app.get(), app.post(), app.put(), app.delete(), app.route()
app.METODO(Ruta, Manejador)
-
Estructura
- app Instanciado de express
- METODO Metodo HTTP de la Ruta
- Soportados: get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search y connect.
- Para todas las rutas usamos app.all()
- Ruta Ruta (url) donde se aplica
- Podemos usar
- Series
- Patrones de Series (Subtipo de Regex)
- Reducido a los subconjuntos ?, +, *, y ()
- Expresiones regulares
- Podemos usar
- Manejador La función que será llamada cuando se alcance la ruta con el método/s correctos/s
- Se puede usar funciones individuales
- Se pueden hacer matrices de funciones
- Se pueden mezclar matrices y funciones individuales
- Argumentos:
- Obj Request de Node.js
- Obj Response de Node.js
- next() Función que dispara el siguiente middleware
-
Métodos HTTP: delimitando a un único método
app.get('/', function (req, res, next) {
res.send('Solo get como método me vale...');
});
- Métodos HTTP: Otra forma de delimitar a un método
app['m-search']('/', function (req, res, next) {
res.send('Solo m-search como método me vale...');
});
- Métodos HTTP: permitiendo todos los métodos
app.all('/', function (req, res, next) {
res.send('Cualquier método me vale...');
});
- Rutas: Raiz http://localhost:8080/
app.get('/', function (req, res, next) {
res.send('Esto es la Raiz');
});
- Rutas: Básicas http://localhost:8080/hola
app.get('/hola', function (req, res, next) {
res.send('Esto es /hola');
});
- Rutas: Capturando Parámetros http://localhost:8080/hola/Eduardo, http://localhost:8080/hola/Oscar...
app.get('/hello/:nombre', function(req, res) {
res.send('Hola ' + req.params.nombre + '!');
});
- Rutas: Capturando varios parámetros http://localhost:8080/desde/Madrid/a/Malga, http://localhost:8080/desde/Madrid/a/NYC
app.get('/desde/:origen/a/:destino', function(req,res,next){
res.send('Quieres ir de ' + req.params.origen + ' a ' + req.params.destino);
});
- Rutas: Capturando varios parámetros y alguno determiando http://localhost:8080/mensaje/1/editar, http://localhost:8080/mensaje/500/borrar...
app.get('/mensaje/:id/:accion(editar|borrar)', function(req,res,next){
res.send('Quieres ' + req.params.accion + ' el mensaje numero ' + req.params.id);
});
- Rutas: Parámetros opcionales http://localhost:8080/user/1/editar, http://localhost:8080/user/500/borrar...
app.get('/user/:id/:comando?', function(req,res,next){
if(req.params.comando){
res.send("Tenemos algo! Quieres " + req.params.comando);
} else {
res.send("Nada de nada...");
}
});
- Rutas: Más parámetros opcionales http://localhost:8080/user/1.pdf, http://localhost:8080/user/500.zip, etc...
app.get('/user/:id.:formato?', function(req,res,next){
if(req.params.formato){
res.send("["+req.params.formato+"] Extensión requerida... ");
} else {
res.send("Sin Extensión requerida");
}
});
- Rutas: Tipo fichero http://localhost:8080/hola.text
app.get('/hola.text', function (req, res) {
res.send('Hola');
});
- Rutas: Patrones de serie (?) http://localhost:8080/acd o http://localhost:8080/abcd
app.get('/ab?cd', function(req, res) {
res.send('ab?cd');
});
- Rutas: Patrones de serie (+) http://localhost:8080/abcd, http://localhost:8080/abbbbbcd, etc...
app.get('/ab+cd', function(req, res) {
res.send('ab+cd');
});
- Rutas: Patrones de serie (*) http://localhost:8080/abcd, http://localhost:8080/abAALGOOOcd, etc...
app.get('/ab*cd', function(req, res) {
res.send('ab*cd');
});
- Rutas: Patrones de serie (()) http://localhost:8080/abe o http://localhost:8080/abcd
app.get('/a(bc)d', function(req, res) {
res.send('a(bc)d');
});
- Rutas: Expresiones regulares http://localhost:8080/mcfly, http://localhost:8080/dragonfly, etc...
app.get(/.*fly$/, function(req, res) {
res.send('/.*fly$/');
});
- Rutas: Molularidad con app.route()
app.route('/pelicula')
.get(function(req, res) {
res.send('todas las peliculas...');
})
.post(function(req, res) {
res.send('Añadir una pelicula...');
})
- Manejadores: Función individual
app.get('/example/a', function (req, res) {
res.send('Hola desde A!');
});
- Manejadores: Dos funciones individuales
app.get('/example/b', function (req, res, next) {
console.log('La respuesta se enviará a la siguiente función...');
next();
}, function (req, res) {
res.send('Hola desde B!');
});
- Manejadores: Matrices
var cb0 = function (req, res, next) {
console.log('CB0');
next();
}
var cb1 = function (req, res, next) {
console.log('CB1');
next();
}
var cb2 = function (req, res) {
res.send('Hola desde C!');
}
app.get('/example/c', [cb0, cb1, cb2]);
- Manejadores: Matrices y funciones individuales
var cb0 = function (req, res, next) {
console.log('CB0');
next();
}
var cb1 = function (req, res, next) {
console.log('CB1');
next();
}
app.get('/example/d', [cb0, cb1], function (req, res, next) {
console.log('La respuesta se enviará a la siguiente función...');
next();
}, function (req, res) {
res.send('Hola desde D!');
});
-
Manejadores: Objeto petición
- req.ip Almacena la IP desde donde se realizó la peticioón
- req.is Que tipo de datos nos llegan desde la petición. Booleano
req.is('json'); req.is('application/*'); req.is('application/json');
- req.params Contenido de la ruta (http://localhost:8080/usuarios/:id)
- req.query Contenido de la consulta de la URL (http://localhost:8080/peliculas?categoria=Ficcion&director=George+Lucas)
app.get('/peliculas', function(req,res,next){ console.log(req.query.director) // George Lucas console.log(req.query.categoria) // Ficcion });
app.get('/peliculas', function(req,res,next){ console.log(req.query.director) // Yo Mismo console.log(req.query.categoria.tipo) // Corto });
- req.body Contenido dentro de la propia petición
-
Manejadores: Métodos de respuesta
-
res.download() Solicita un archivo para descargarlo.
-
res.end() Finaliza el proceso de respuesta.
-
res.json() Envía una respuesta JSON.
-
res.jsonp() Envía una respuesta JSON con soporte JSONP.
- Valores por defecto ajustables en app.set()
- ?callback= valor por defecto en la petición
res.jsonp({date: newDate()});
- Valores por defecto ajustables en app.set()
-
res.redirect() Redirecciona una solicitud.
-
res.render() Renderiza una plantilla a la que le pasa además datos (opcional)
-
res.send() Envía una respuesta de varios tipos.
- Muy flexible
- Código y contenido
res.send(404,'Oops...');
- Enviar un JSON
res.send({mensaje: "secreto"});
- Solo código (autocompleta el mensaje)
res.send(200);
- Código y contenido
- Muy flexible
-
res.sendFile() Envía un archivo para ser visualizado.
-
res.sendStatus() Envia un estado HTTP.
-
Mecánica: app.use()
- Usando Middleware: app.use(middleware)
- Declaración directa
app.use(function(req, res, next) { console.log("Petición en "+req.url+" con el método" + req.method); next(); });
- Enlazando
var express = require('express'); var app = express(); function chivato (req, res, next) { console.log("Nueva petición en "+req.url+" con el método" + req.method); next(); }; app.use(chivato); app.get('/', function (req, res) { res.send('Hola a todos!'); }); app.listen(3000);
Middleware: La clave
Una función que recibe 3 o 4 parámetros: req, res, next
-
Tipos:
- Middleware de nivel de aplicación
- Middleware de nivel de direccionador
- Middleware de manejo de errores
- Middleware incorporado
- Middleware de terceros
-
Concatenación:
- Al terminar su tarea, tiene que invocar a next()
Middleware de nivel de aplicación
- Global Afecta a cualquier Ruta
var app = express();
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
- Punto de Montaje Afecta solo a una vía de acceso, en este caso /user/:id
var app = express();
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id);
next();
}, function (req, res, next) {
res.send('User Info');
});
Middleware de nivel de direccionador
- Global Afecta a cualquier Ruta
var app = express();
var router = express.Router();
router.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
- Punto de Montaje Afecta solo a una vía de acceso, en este caso /user/:id
var app = express();
var router = express.Router();
router.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id);
next();
}, function (req, res, next) {
res.send('User Info');
});
Middleware de manejo de errores
- Argumento adiccional
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(gestionErrorServer);
function gestionErrorServer(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
res.status(500);
res.render('error', { error: err });
}
}
//...
Middleware incorporado
-
Desde la versión 4.x Express no depende de Connect
-
Solamente queda incorporado express.static
- Incluyendo archivos estáticos
app.use(express.static('public'));
- Configurando la carpeta pública
var options = { dotfiles: 'ignore', etag: false, extensions: ['htm', 'html'], index: false, maxAge: '1d', redirect: false, setHeaders: function (res, path, stat) { res.set('x-timestamp', Date.now()); } } app.use(express.static('public', options));
- Usando múltiples directorios estáticos
app.use(express.static('public')); app.use(express.static('uploads')); app.use(express.static('files'));
-
Middleware oficial (No incorporado):
- serve-favicon
- Sirve el favicon
- Morgan
- Logger para peticiones HTTP
- body-parser
- Decodifica:
- application/json
- application/x-www-form-urlencoded
- multipart/form-data
- Crea el objeto req.body con los parámetros
- Crea el objeto req.files con los ficheros que se han subido desde un formulario
- Decodifica:
- basic-auth-connect
- Protección básica de las rutas usando usuario y contraseña
- csurf
- Crea req.session._csrf
- Protección contra Cross-site request forgery
- Usando tokens
- cors
- Gestión de Cross Origin Resource Sharing (CORS)
- compression
- express-session
- Simple gestor de sesiones
- multer
- Node.js middleware for handling
multipart/form-data
.
- Node.js middleware for handling
- cookie-session
- Simple cookie-based session middleware
- cookie-parser
- cookie parsing middleware
- Crea req.cookies
- cookie-session
- Inicializa y parsea los datos de sesión del usuario
- Utilizando cookies como almacenamiento
- Algunas opciones avanzadas
- serve-static
- Serve static files
- ¡Muy útil! Se pone cerca del final
- Cachea los ficheros
- La variable global __dirname contiene el directorio donde reside el script en ejecución
- vhost
- Virtual Domain Hosting
- restful-router
- Simple RESTful url router.
- connect-markdown
- Auto convert markdown to html for connect.
- serve-favicon
Middleware: Más Middleware, más funcionalidad
-
Habilitando CORS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
- Baneando Navegadores (IE8 y anteriores)
// banear.js
var banned = [ 'MSIE', 'Trident/6.0'];
module.exports = function(){
return function(req, res, next) {
if (req.headers['user-agent'] !== undefined && req.headers['user-agent'].indexOf(banned) > -1) {
res.end('Navegador no compatible');
} else {
next();
}
}
};
- Mapeado de parámetros en rutas.
- Cuidado con app.param()
- Funcionamiento (orden de busqueda):
- req.params
- req.body
- req.query
- Funcionamiento (orden de busqueda):
- Cuidado con app.param()
app.param('id_usuario',function(req,res,next,id){
// Llamamos a una función que valida....
validadorUsuario(id, function(err, usuario){
if (err) {
next(err);
} else if (user) {
// Actualizamos con los nuevos datos
req.params.usuario = usuario;
// damos paso a la siguiente función
next();
} else {
next(new Error('Error al cargar el usuario'));
}
});
});
- Redireccionando al usuario en caso de no estar logados
module.exports = function(req,res){
if (req.params.usuario.logged){
next();
} else {
res.redirect('/login');
}
};
- Gestion de errores tipo 4xx y tipo 5xx
// Error 404
app.use(function(req, res) {
res.status(400);
res.render('404', { title: '404 - No encontrado' });
});
// Error 500 (solo en caso de desarrollo)
app.use('development', function(error, req, res, next) {
res.status(500);
res.render('500', {
title: 'Jefe, ¡Tenemos un 500!',
error: error
});
});
// Error 500 (solo en caso producción)
app.use('production', function(error, req, res, next) {
res.status(500);
res.render('500', {
title: 'Oops… ¡Algo salió mal!'
});
// Mandamos un email con los datos al equipo.
});