Skip to content

Latest commit

 

History

History
executable file
·
1072 lines (849 loc) · 27 KB

File metadata and controls

executable file
·
1072 lines (849 loc) · 27 KB

Express

Express_logo

Influencias / usos

  • Otros frameworks similares:

    • Zend (PHP)
    • Django (Python)
    • Sinatra (Ruby)
  • Uso:

    • API JSON
    • Single Page Applications
    • App tiempo real

Pros

  • Rutas
  • Parámetros
  • Formularios y subida de ficheros
  • Cookies
  • Sesiones
  • Templates
  • Middlewares

Contras

  • Base de datos / ORM
  • Autenticación de usuarios
  • Seguridad
  • Migraciones
  • Deployment
  • Organización del código

Migraciones

Instalación

  • Instalación local:

    npm install express
    
  • Instalación global:

    npm install -g express
    
  • Instalación versiones anteriores:

    npm install -g express@3.x
    

Hello World!

const express= require('express');
const app = express();

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(8080, () => console.log('Example app listening on port 8080'));

Generador de Express

  • Instalación global del generador

    npm install express-generator -g
    
  • Opciones de instalación

    express -h
    
  • Generar un proyecto (genera un directorio)

    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.set('estado_aplicacion', '*');
      • Solo desarrollo

        NODE_ENV=development node app.js
        
        if (NODE_ENV === 'develop')
          app.set('estado_aplicacion', 'development');
        }
      • Solo producción

        NODE_ENV=production node app.js
        
        if (NODE_ENV === 'production')
          app.set('estado_aplicacion', '*');
        }
      • Solo personalizado

        NODE_ENV=personalizado1 node app.js
        
        if (NODE_ENV === 'personalizado1')
          app.set('estado_aplicacion', '*');
        }
    • Motores de Plantillas

      • Variables locales (solo disponibles para las plantillas)

        // Guardando
        app.locals.title = 'My App';
        app.locals.email = 'me@myapp.com';
        
        // Usando
        app.locals.title // My App
        app.locals.email // me@myapp.com
      • Definiendo el sistema de plantillas que usaremos

        // npm install pug --save
        const express = require('express');
        const app = express();
        
        app.set('view engine', 'pug');
        
        app.get('/', (req, res) => {
            res.render('index', { title: 'Hey', message: 'Hello there!'});
        });
        
        app.listen(8080);
  • 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('/', (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']('/', (req, res, next) => {
      res.send('Solo m-search como método me vale...');
    });
  • Métodos HTTP: permitiendo todos los métodos

    app.all('/', (req, res, next) => {
      res.send('Cualquier método me vale...');
    });
  • Rutas: Raiz http://localhost:8080/

    app.get('/', (req, res, next) => {
      res.send('Esto es la Raiz');
    });
  • Rutas: Básicas http://localhost:8080/hola

    app.get('/hola', (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', (req, res) => {
        res.send('Hola ' + req.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', (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)', (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?', (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?', (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', (req, res) => {
      res.send('Hola');
    });
  • Rutas: Patrones de serie (?) http://localhost:8080/acd o http://localhost:8080/abcd

    app.get('/ab?cd', (req, res) => {
      res.send('ab?cd');
    });
  • Rutas: Patrones de serie (+) http://localhost:8080/abcd, http://localhost:8080/abbbbbcd, etc...

    app.get('/ab+cd', (req, res) => {
      res.send('ab+cd');
    });
  • Rutas: Patrones de serie (*) http://localhost:8080/abcd, http://localhost:8080/abAALGOOOcd, etc...

    app.get('/ab*cd', (req, res) => {
      res.send('ab*cd');
    });
  • Rutas: Patrones de serie (()) http://localhost:8080/abe o http://localhost:8080/abcd

    app.get('/a(bc)d', (req, res) => {
      res.send('a(bc)d');
    });
  • Rutas: Expresiones regulares http://localhost:8080/mcfly, http://localhost:8080/dragonfly, etc...

    app.get(/.*fly$/, (req, res) => {
      res.send('/.*fly$/');
    });
  • Rutas: Molularidad con app.route()

    app.route('/pelicula')
      .get((req, res) => {
        res.send('todas las peliculas...');
      })
      .post((req, res) => {
        res.send('Añadir una pelicula...');
      })
  • Manejadores: Función individual

    app.get('/example/a', (req, res) => {
      res.send('Hola desde A!');
    });
  • Manejadores: Dos funciones individuales

    app.get('/example/b', (req, res) => {
      console.log('La respuesta se enviará a la siguiente función...');
      next();
    }, (req, res) => {
      res.send('Hola desde B!');
    });
  • Manejadores: Matrices

    const cb0 = (req, res, next) => {
      console.log('CB0');
      next();
    };
    
    const cb1 = (req, res, next) => {
      console.log('CB1');
      next();
    };
    
    const cb2 = (req, res) => {
      res.send('Hola desde C!');
    };
    
    app.get('/example/c', [cb0, cb1, cb2]);
  • Manejadores: Matrices y funciones individuales

    const cb0 = (req, res, next) => {
      console.log('CB0');
      next();
    };
    
    const cb1 = (req, res, next) => {
      console.log('CB1');
      next();
    };
    
    app.get('/example/d', [cb0, cb1], (req, res, next) => {
      console.log('La respuesta se enviará a la siguiente función...');
      next();
    }, (req, res) => {
      res.send('Hola desde D!');
    });
  • Manejadores: Objeto 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()});
    • 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);
    • res.sendFile() Envía un archivo para ser visualizado.

    • res.sendStatus() Envía un archivo para ser descargado.

Mecánica: app.use()

  • Usando Middleware: app.use(middleware)
    • Declaración directa

      app.use((req, res, next) => {
          console.log("Petición en "+req.url+" con el método" + req.method);
          next(); 
      });
    • Enlazando

      const express = require('express');
      const 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('/', (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

    const app = express();
    
    app.use((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

    const app = express();
    
    app.get('/user/:id', (req, res, next) => {
      console.log('ID:', req.params.id);
      next();
    }, (req, res, next) => {
      res.send('User Info');
    });

Middleware de nivel de direccionador

  • Global Afecta a cualquier Ruta

    const app = express();
    const router = express.Router();
    
    router.use((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

    const app = express();
    const router = express.Router();
    
    router.get('/user/:id', (req, res, next) => {
      console.log('ID:', req.params.id);
      next();
    }, (req, res, next) => {
      res.send('User Info');
    });

Middleware de manejo de errores

  • Argumento adiccional

    const bodyParser = require('body-parser');
    const 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

      const options = {
        dotfiles: 'ignore',
        etag: false,
        extensions: ['htm', 'html'],
        index: false,
        maxAge: '1d',
        redirect: false,
        setHeaders(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

  • La lista de Raynos

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

    // ban.js
    const banned = [ 'MSIE', 'Trident/6.0'];
    
    module.exports = () => (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', (req, res, next, id) =>{
        // Llamamos a una función que valida....
        validadorUsuario(id, (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 = (req, res, next) => {
  if (req.params.usuario.logged){
    next();
  } else {
    res.redirect('/login');
  }
};
  • Gestion de errores tipo 4xx y tipo 5xx

    // Error 404
    app.use((req, res) => {
      res.status(400);
      res.render('404', { title: '404 - No encontrado' });
    });
    
    // Error 500 (solo en caso de desarrollo)
    app.use('development', (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', (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

Express en resumen

  • GET Básico

    app.get('/hola', (req, res) => {
        res.send('hemos abierto una nueva ruta!');
    });
  • POST Básico

    • app.js

      app.post('/', (req, res) => {
        res.send(req.body);
      });
    • index.jade

      section.container
      h1 Manda tu mensaje!
      
      form(method='post', action='/')
        fieldset
          legend Mandame tu mensaje:
          p.contenido
            label Tu mensaje
            input(name='mensaje')
          p.acciones
            input(type='submit', value='Enviar')
    • respuesta

      {
        "mensaje": "Hola Cracks!"
      }
  • Parámetros en las rutas

    • app.js

      app.get('/hola/:usuario', (req, res) => {
          res.send(`Hola ${req.params.usuario}. Hemos abierto una nueva ruta personalizada!`);
      });
  • Simulando una respuesta de una base de datos en las rutas:

    • app.js

      app.get('/consulta/:usuario', (req, res) => {
        const datos = {
          marca: "Seat",
          modelo: "Ibiza",
          edad: 20,
          ITVPasada: true,
          seguroPasado: true
        };
        
        res.render('consulta.jade', {usuario: req.params.usuario, datos: datos});
      });
    • consulta.jade

    .datos
      h3 Datos: 
      p= "El Coche de "+usuario+" es un "+datos.marca+" "+datos.modelo
        
      h3 Detalles técnicos:
      ul
          li= "ITV en regla: " +datos.ITVPasada 
          li= "seguro en regla: " +datos.seguroPasado

Utilidades

PUG (antes Jade)

Jade_logo

Jade... ya no se llama jade. Ahora se llama PUG

PUG_logo

Implementaciones en otros lenguajes

  • php

  • scala

  • ruby

  • python

  • java

  • Entendiendo la mécanica

    • index.jade:

      doctype html
      html(lang="en")
        head
          title= pageTitle
          script(type='text/javascript').
            if (foo) {
               bar(1 + 5)
            }
        body
          h1 Jade - node template engine
          #container.col
            if youAreUsingJade
              p You are amazing
            else
              p Get on it!
            p.
              Jade is a terse and simple
              templating language with a
              strong focus on performance
              and powerful features.
    • index.html:

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <title>Jade</title>
          <script type="text/javascript">
            if (foo) {
               bar(1 + 5)
            }
          </script>
        </head>
        <body>
          <h1>Jade - node template engine</h1>
          <div id="container" class="col">
            <p>You are amazing</p>
            <p>
              Jade is a terse and simple
              templating language with a
              strong focus on performance
              and powerful features.
            </p>
          </div>
        </body>
      </html>
  • Bootstrap:

    • index.jade:

      doctype html
      html
        head
          title title
          include ./includes/styles.jade
        body
          .row
            .container
              .jumbotron
                h1 Hola, desde Bootstrap!
                p ¿Qué te parece?
                p
                  a.btn.btn-primary.btn-lg(href='http://getbootstrap.com/', role='button') Aprende más!
          include ./includes/scripts.jade
    • includes/styles.jade

      //- includes/styles.jade
      // Bootstrap
      link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css')
    • includes/scripts.jade

      //- includes/scripts.jade
      // Jquery
      script(src='//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js')
      // Bootstrap
      script(src='//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js')
    • index.html

      <!DOCTYPE html>
      <html>
        <head>
          <title>title</title>
          <!-- Bootstrap-->
          <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
          </head>
        <body>
          <div class="row">
            <div class="container">
              <div class="jumbotron">
                <h1>Hello, desde Bootstrap!</h1>
                <p>¿Qué te parece?</p>
                <p>
                    <a href="http://getbootstrap.com/" role="button" class="btn btn-primary btn-lg">Aprende más!</a>
                </p>
              </div>
            </div>
          </div>
          <!-- Jquery-->
          <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
          <!-- Bootstrap-->
          <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        </body>
      </html>
  • Pug - Github

  • Pug - Getting Started

  • Jade Syntax Documentation by example

Ejercicios

1 - Crearemos una aplicación utilizando Express para gestionar las películas que nos gustan.

Ver en el siguiente repositorio