Introducción a Express.js:parámetros, manejo de errores y otro middleware

Nota: Este texto es parte de Express.js Guide:The Comprehensive Book on Express.js.

Express.js es uno de los marcos Node.js más populares y maduros. Puede obtener más información en Introducción a Express.js. serie en webapplog.com:

  • Introducción a Express.js:aplicación API REST simple con Monk y MongoDB
  • Tutorial de Node.js MVC:Express.js + Derby Hello World

Para saber cómo crear una aplicación desde cero, consulte la publicación anterior.

Controladores de solicitudes

Express.js es un marco de node.js que, entre otras cosas, proporciona una forma de organizar rutas. Cada ruta se define a través de una llamada de método en un objeto de aplicación con un patrón de URL como primer parámetro (también se admite RegExp), por ejemplo:

app.get('api/v1/stories/', function(res, req){
  ...
})

o, para un método POST:

app.post('/api/v1/stories'function(req,res){
  ...
})

No hace falta decir que los métodos DELETE y PUT también son compatibles.
Las devoluciones de llamada que pasamos a get() o post() Los métodos se denominan controladores de solicitudes, porque aceptan solicitudes (req ), procesarlos y escribir en la respuesta (res ) objetos. Por ejemplo:

app.get('/about', function(req,res){
  res.send('About Us: ...');
});

Podemos tener múltiples controladores de solicitudes, de ahí el nombre middleware . Aceptan un tercer parámetro next llamando a cuál (next() ) cambiará el flujo de ejecución al siguiente controlador:

app.get('/api/v1/stories/:id', function(req,res, next) {
  //do authorization
  //if not authorized or there is an error 
  // return next(error);
  //if authorized and no errors
  return next();
}), function(req,res, next) {
  //extract id and fetch the object from the database
  //assuming no errors, save story in the request object
  req.story = story;
  return next();
}), function(req,res) {
  //output the result of the database search
  res.send(res.story);
});

El ID de una historia en el patrón de URL es un parámetro de cadena de consulta que necesitamos para encontrar elementos coincidentes en la base de datos.

Middleware de parámetros

Los parámetros son valores pasados ​​en una cadena de consulta de una URL de la solicitud. Si no tuviéramos Express.js o una biblioteca similar, y tuviéramos que usar solo los módulos principales de Node.js, tendríamos que extraer parámetros del objeto HTTP.request a través de algún require('querystring').parse(url) o require('url').parse(url, true) trucos de funciones.

Gracias al marco Connect y a la gente de VisionMedia, Express.js ya tiene soporte para parámetros, manejo de errores y muchas otras características importantes en forma de middleware. Así es como podemos conectar el middleware param en nuestra aplicación:

app.param('id', function(req,res, next, id){
  //do something with id
  //store id or other info in req object
  //call next when done
  next();
});

app.get('/api/v1/stories/:id',function(req,res){
  //param middleware will be execute before and
  //we expect req object already have needed info
  //output something
  res.send(data);
});

Por ejemplo:

app.param('id', function(req,res, next, id){
  req.db.get('stories').findOne({_id:id}, function (e, story){
    if (e) return next(e);
    if (!story) return next(new Error('Nothing is found'));
    req.story = story;
    next();
  });
});

app.get('/api/v1/stories/:id',function(req,res){
  res.send(req.story);
});

O podemos usar múltiples controladores de solicitudes pero el concepto sigue siendo el mismo:podemos esperar tener req.story objeto o un error arrojado antes de la ejecución de este código, por lo que abstraemos el código/lógica común para obtener parámetros y sus respectivos objetos:

app.get('/api/v1/stories/:id', function(req,res, next) {
  //do authorization
  }),
  //we have an object in req.story so no work is needed here
  function(req,res) {
  //output the result of the database search
  res.send(story);
});

La autorización y el saneamiento de entrada también son buenos candidatos para residir en los middlewares.

Función param() es especialmente genial porque podemos combinar diferentes claves, por ejemplo:

app.get('/api/v1/stories/:storyId/elements/:elementId',function(req,res){
  res.send(req.element);
});

Gestión de errores

El manejo de errores generalmente se usa en toda la aplicación, por lo tanto, es mejor implementarlo como un middleware. Tiene los mismos parámetros más uno más, error :

[Nota al margen]

Leer publicaciones de blog es bueno, pero ver cursos en video es aún mejor porque son más atractivos.

Muchos desarrolladores se quejaron de la falta de material de video de calidad asequible en Node. Es una distracción ver videos de YouTube y una locura pagar $ 500 por un curso de video de Node.

Visite Node University, que tiene cursos de video GRATUITOS en Node:node.university.

[Fin de la nota al margen]

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  res.send(500);
})

De hecho, la respuesta puede ser cualquier cosa:

Cadena JSON

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  res.send(500, {status:500, message: 'internal error', type:'internal'});
})

Mensaje de texto

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  res.send(500, 'internal server error');
})

Página de error

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  //assuming that template engine is plugged in
  res.render('500');
})

Redirigir a la página de error

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  res.redirect('/public/500.html');
})

Estado de respuesta HTTP de error (401, 400, 500, etc.)

app.use(function(err, req, res, next) {
  //do logging and user-friendly error message display
  res.end(500);
})

Por cierto, ¡el registro también debe abstraerse en un middleware!

Para desencadenar un error desde los controladores de solicitudes y el middleware, simplemente puede llamar a:

next(error);

o

next(new Error('Something went wrong :-(');

También puede tener múltiples controladores de errores y usar funciones con nombre en lugar de funciones anónimas como se muestra en la guía de manejo de errores de Express.js.

Otro software intermedio

Además de extraer parámetros, se puede usar para muchas cosas, como autorización, manejo de errores, sesiones, salida y otros.

res.json() es uno de ellos. Convenientemente genera el objeto JavaScript/Node.js como un JSON. Por ejemplo:

app.get('/api/v1/stories/:id', function(req,res){
  res.json(req.story);
});

es equivalente a (si req.story es una matriz y un objeto):

app.get('/api/v1/stories/:id', function(req,res){
  res.send(req.story);
});

o

app.get('api/v1/stories/:id',function(req,res){
  res.set({
    'Content-Type': 'application/json'
  });
  res.send(req.story);
});

Abstracción

El middleware es flexible. Puede usar funciones anónimas o con nombre, pero lo mejor es abstraer los controladores de solicitudes en módulos externos según la funcionalidad:

var stories = require.('./routes/stories');
var elements = require.('./routes/elements');
var users = require.('./routes/users');
...
app.get('/stories/,stories.find);
app.get('/stories/:storyId/elements/:elementId', elements.find);
app.put('/users/:userId',users.update);

rutas/historias.js:

module.exports.find = function(req,res, next) {
};

rutas/elementos.js:

module.exports.find = function(req,res,next){
};

rutas/usuarios.js:

module.exports.update = function(req,res,next){
};

Puedes usar algunos trucos de programación funcional, como este:

function requiredParamHandler(param){
  //do something with a param, e.g., check that it's present in a query string
  return function (req,res, next) {
    //use param, e.g., if token is valid proceed with next();
    next();
  });
}

app.get('/api/v1/stories/:id', requiredParamHandler('token'), story.show);

var story  = {
  show: function (req, res, next) {
    //do some logic, e.g., restrict fields to output
    return res.send();
  }
}   

Como puede ver, el middleware es un concepto poderoso para mantener el código organizado. La mejor práctica es mantener el enrutador ligero y delgado moviendo toda la lógica a los módulos/archivos externos correspondientes. De esta manera, los parámetros importantes de configuración del servidor estarán ordenados en un solo lugar, ¡justo allí cuando los necesite! :-)