Skip to content

Latest commit

 

History

History
761 lines (664 loc) · 22.1 KB

File metadata and controls

761 lines (664 loc) · 22.1 KB

shieldsIO shieldsIO shieldsIO

WideImg

Master en Programación de Aplicaciones con JavaScript y Node.js

JS, Node.js, Frontend, Express, Patrones, IoT, HTML5_APIs, Asincronía, Websockets, ECMA6, ECMA7

Clase 75

Express

Express_logo

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
      
  • Opcional: Volviendo el arranque al estilo Express 3.x

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!'});
        });
  • Comparativa de Motores de plantillas

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
    • 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...');
  });
  app.get('/', function (req, res, next) {
    res.send('Esto es la Raiz');
  });
  app.get('/hola', function (req, res, next) {
    res.send('Esto es /hola');
  });
  app.get('/hello/:nombre', function(req, res) {
      res.send('Hola ' + req.params.nombre + '!');
  });
    app.get('/desde/:origen/a/:destino', function(req,res,next){
      res.send('Quieres ir de ' + req.params.origen + ' a ' + req.params.destino);
    });
    app.get('/mensaje/:id/:accion(editar|borrar)', function(req,res,next){
      res.send('Quieres ' + req.params.accion + ' el mensaje numero ' + req.params.id);
    });
  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...");
    }
  });
  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");
    }
  });
  app.get('/hola.text', function (req, res) {
    res.send('Hola');
  });
  app.get('/ab?cd', function(req, res) {
    res.send('ab?cd');
  });
  app.get('/ab+cd', function(req, res) {
    res.send('ab+cd');
  });
  app.get('/ab*cd', function(req, res) {
    res.send('ab*cd');
  });
  app.get('/a(bc)d', function(req, res) {
    res.send('a(bc)d');
  });
  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!');
  });

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

Mw_schema

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
    • 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.
    • 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.

Middleware: Más Middleware, más funcionalidad

  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
  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.
});

Ejemplos con Express