Skip to content

Latest commit

 

History

History
867 lines (722 loc) · 29.9 KB

File metadata and controls

867 lines (722 loc) · 29.9 KB

shieldsIO shieldsIO shieldsIO

WideImg

Máster en Programación FullStack con JavaScript y Node.js

JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing

Clase 67

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

Documentación

Recursos

Express: Migraciones

De Express 3.x a Express 4.x

De Express 4.x a Express 5.x (hoy es Alpha)

Express: Refrescando conceptos

http/s: peticion, respuesta, codigo de estado y verbos...

IMG

Partes de una URL

┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│                                            href                                             │
├──────────┬──┬─────────────────────┬─────────────────────┬───────────────────────────┬───────┤
│ protocol │  │        auth         │        host         │           path            │ hash  │
│          │  │                     ├──────────────┬──────┼──────────┬────────────────┤       │
│          │  │                     │   hostname   │ port │ pathname │     search     │       │
│          │  │                     │              │      │          ├─┬──────────────┤       │
│          │  │                     │              │      │          │ │    query     │       │
"  https:   //    user   :   pass   @ sub.host.com : 8080   /p/a/t/h  ?  query=string   #hash "
│          │  │          │          │   hostname   │ port │          │                │       │
│          │  │          │          ├──────────────┴──────┤          │                │       │
│ protocol │  │ username │ password │        host         │          │                │       │
├──────────┴──┼──────────┴──────────┼─────────────────────┤          │                │       │
│   origin    │                     │       origin        │ pathname │     search     │ hash  │
├─────────────┴─────────────────────┴─────────────────────┴──────────┴────────────────┴───────┤
│                                            href                                             │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
(all spaces in the "" line should be ignored — they are purely for formatting)

Express: Instalación

  • Instalación Global:

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

    npm install -g [email protected]
    

Express: 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);
});

Express: Generador de Express

⚠️ IMPORTANTE

El generador de express sigue un esquema www/var que no se corresponde con proyectos reales. Solo tienes que seguir estos pasos para que arranque al estilo Express 3.x

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

Express: Partes Claves

Express: Variables locales de Express

Mecánica: app.set()

  • Nos permite establecer la configuración de Express
  • Podemos almacenar datos personalizados de manera global

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(() => {
  app.set('estado_aplicacion', '*');
});
  • Solo desarrollo
NODE_ENV=development node app.js
app.configure('development', () => {
  app.set('estado_aplicacion', 'development');
});
  • Solo producción
NODE_ENV=production node app.js
app.configure('production', () => {
  app.set('estado_aplicacion', 'production');
});
  • Solo personalizado
NODE_ENV=personalizado1 node app.js
app.configure('personalizado1', () => {
  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
const express = require('express'),
  jade = require('jade'),
  app = express();
  
app.set('view engine', 'jade');

app.get('/', (req, res) => {
  res.render('index', { title: 'Hey', message: 'Hello there!'});
});

Comparativa de Motores de plantillas

Express: Gestión de métodos HTTP

app.all(), app.get(), app.post(), app.put(), app.delete(), app.route(), etc...

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

delimitando a un único método

app.get('/', (req, res, next) => {
  res.send('Solo get como método me vale...');
});

Otra forma de delimitar a un método

app['m-search']('/', (req, res, next) => {
  res.send('Solo m-search como método me vale...');
});

Permitiendo todos los métodos

app.all('/', (req, res, next) => {
  res.send('Cualquier método me vale...');
});

Express: Gestión de Rutas

Raiz http://localhost:8080/

app.get('/', (req, res, next) => {
  res.send('Esto es la Raiz');
});

Básicas http://localhost:8080/hola

app.get('/hola', (req, res, next) => {
  res.send('Esto es /hola');
});

Capturando Parámetros http://localhost:8080/hola/Eduardo, http://localhost:8080/hola/Oscar...

app.get('/hello/:nombre', (req, res) => {
    res.send(`Hola, ${req.params.nombre}!`);
});

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

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

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

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

Tipo fichero http://localhost:8080/hola.text

app.get('/hola.text', (req, res) => {
  res.send('Hola');
});

Patrones de serie (?) http://localhost:8080/acd o http://localhost:8080/abcd

app.get('/ab?cd', (req, res) => {
  res.send('ab?cd');
});

Patrones de serie (+) http://localhost:8080/abcd, http://localhost:8080/abbbbbcd, etc...

app.get('/ab+cd', (req, res) => {
  res.send('ab+cd');
});

Patrones de serie (*) http://localhost:8080/abcd, http://localhost:8080/abAALGOOOcd, etc...

app.get('/ab*cd', (req, res) => {
  res.send('ab*cd');
});

Patrones de serie (()) http://localhost:8080/abe o http://localhost:8080/abcd

app.get('/a(bc)d', (req, res) => {
  res.send('a(bc)d');
});

Expresiones regulares http://localhost:8080/mcfly, http://localhost:8080/dragonfly, etc...

app.get(/.*fly$/, (req, res) => {
  res.send('/.*fly$/');
});

Express: Manejadores de Rutas

Función individual

app.get('/example/a', (req, res) => {
  res.send('Hola desde A!');
});

Manejadores: Dos funciones individuales

app.get('/example/b', (req, res, next) => {
  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!');
});

Express: La petición

Destacado

Ejemplo de req.is

req.is('json');
req.is('application/*');
req.is('application/json');

Ejemplo de req.query

// http://localhost:8080/peliculas?categoria=Ficcion&director=George+Lucas
app.get('/peliculas', (req, res, next) => {
  console.log(req.query.director) // George Lucas
  console.log(req.query.categoria) // Ficcion
});

// http://localhost:8080/peliculas?categoria[tipo]=Corto&director=Yo+Mismo
app.get('/peliculas', (req, res, next) => {
  console.log(req.query.director) // Yo Mismo
  console.log(req.query.categoria.tipo) // Corto
});

Express: La respuesta

Destacado

  • 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(): Envia un estado HTTP.
  • Y mucho más...

Express: Middelware

Middleware: La clave

Mw_schema

Patrón del middelware

  • Todas las funciones reciben tres parámetros request, response y next
  • Es una secuencia de callbacks que se autogestionan
  • Si todo sale correctamente, nuestra función debe invocar a la siguiente usando next()
  • Si algo va mal, debe gestionar la salida con response.
  • Puede resultar complejo saber cuantas funciones quedan o han pasado.
  • Es un esquema de cascada y delegación de resposabildiad muy claro.

Tipos de Middelware

  • Middleware de nivel de aplicación
  • Middleware de nivel de direccionador
  • Middleware de manejo de errores
  • Middleware incorporado
  • Middleware de terceros

Express: Middelware con app.use()

Esto funciona nivel de toda la aplicación

//import express from 'express';
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);  

Express: Middelware por niveles

Middleware Global, a nivel de toda la aplicación

const app = express();

app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

Middelware de ruta, 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');
});

Express: manejo de errores

Hacemos uso de un argumento adiccional, err

//import bodyParser from 'body-parser';
//import methodOverride from 'method-override';
const bodyParser = require('body-parser');
const methodOverride = require('method-override');

app.use(gestionErrorServer);

function gestionErrorServer(err, req, res, next) {
  //Soporte para llamdas AJAX (xhr)
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' });
  } else {
    res.status(500);
    res.render('error', { error: err });
  }
}
//...

Express: Gestión de estáticos

Importante

  • Desde la versión 4.x Express no depende de Connect
  • Todo el middelware que habia en Express 3.x, es sacado del core de express 4.x
  • 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'));

Express: Middelware en la prática

Habilitando CORS

app.use((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
const banned = [ 'MSIE', 'Trident/6.0'];

export default () => (req, res, next) => {
  if (req.headers['user-agent'] !== undefined && req.headers['user-agent'].includes(banned)) {
          res.end('Navegador no compatible');
  } else { 
    next(); 
  } 
};

Redireccionando al usuario en caso de no estar logeados

export default (req, res) => {
  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(404);
  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: 'Panico en la sala!!... Tenemos un 500... o.O',
  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.
});

Express: Objeto Router

Molularidad con app.route()

app.route('/pelicula')
  .get((req, res) => {
    res.send('todas las peliculas...');
  })
  .post((req, res) => {
    res.send('Añadir una pelicula...');
  })

Middleware de nivel Global

const app = express();
const router = express.Router();

router.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

Middelware a nivel de ruta, 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');
});

Ejercicios

1 - Crea tu primer script de Express

Objetivos

  • Crear un proxy para hacer llamadas AJAX GET que no tengan CORS Habilitado /cors?url=...
  • Crear un sistema de estaticos para que ./public sea accesible
  • poner como root (/) la ruta ./public/index.html
  • Lanzar todo con el comando npm start
// npm install express request
var express = require('express');
var request = require('request');
var app = express();
var path = require('path');

// Static files
app.use(express.static('public'));

// viewed at http://localhost:8080
app.get('/', function(req, res) {
    res.sendFile(path.join(__dirname + '/public/index.html'));
});

app.get('/proxy', function (req, res){
    // @Example: localhost:8080/proxy?url=http://airemad.com/api/v1/station/
    
    // CORS
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    
    // PIPE Response
    request({  
        url: req.query.url,
        method: "GET"
    }).pipe(res)
});

app.listen(process.env.PORT || 8080);