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! :-)