Construir Aplicaciones Web Usando Node.js
Spanish (Español) translation by James (you can also view the original English article)
Introducción
Aparte de construcción API, Node.js es ideal para crear aplicaciones web estándar. Tiene potentes herramientas para satisfacer el gusto de los desarrolladores web. En este tutorial, construir una aplicación web que puede servir como una biblioteca.
Edificio para conocer algunos tipos de middleware, verá cómo gestionar el envío del formulario en Node.js, y usted también podrá hacer referencia a dos modelos.
Vamos a empezar.
Para Empezar
Empiece instalando el generador expreso en su máquina.
1 |
npm install express-generator -g |
Ejecute el comando expreso generador para generar la aplicación.
1 |
express tutsplus-library --view=pug |
1 |
create : tutsplus-library |
2 |
create : tutsplus-library/package.json |
3 |
create : tutsplus-library/app.js |
4 |
create : tutsplus-library/public |
5 |
create : tutsplus-library/routes |
6 |
create : tutsplus-library/routes/index.js |
7 |
create : tutsplus-library/routes/users.js |
8 |
create : tutsplus-library/views |
9 |
create : tutsplus-library/views/index.pug |
10 |
create : tutsplus-library/views/layout.pug |
11 |
create : tutsplus-library/views/error.pug |
12 |
create : tutsplus-library/bin |
13 |
create : tutsplus-library/bin/www |
14 |
create : tutsplus-library/public/javascripts |
15 |
create : tutsplus-library/public/images |
16 |
create : tutsplus-library/public/stylesheets |
17 |
create : tutsplus-library/public/stylesheets/style.css |
18 |
|
19 |
install dependencies:
|
20 |
$ cd tutsplus-library && npm install |
21 |
|
22 |
run the app:
|
23 |
$ DEBUG=tutsplus-library:* npm start |
Ahora emigran a su trabajo, abra package.json y las dependencias similares a lo que tengo debajo.
1 |
#package.json |
2 |
|
3 |
{
|
4 |
"name": "tutsplus-library", |
5 |
"version": "0.0.0", |
6 |
"private": true, |
7 |
"scripts": { |
8 |
"start": "node ./bin/www" |
9 |
},
|
10 |
"dependencies": { |
11 |
"body-parser": "~1.17.1", |
12 |
"connect-flash": "^0.1.1", |
13 |
"cookie-parser": "~1.4.3", |
14 |
"debug": "~2.6.3", |
15 |
"express": "~4.15.2", |
16 |
"express-messages": "^1.0.1", |
17 |
"express-session": "^1.15.5", |
18 |
"express-validator": "^4.2.1", |
19 |
"mongoose": "^4.11.12", |
20 |
"morgan": "~1.8.1", |
21 |
"pug": "~2.0.0-beta11", |
22 |
"serve-favicon": "~2.4.2" |
23 |
}
|
24 |
}
|
Ejecute el comando para instalar los paquetes.
1 |
npm install
|
Configurar el Archivo de Entrada
app.js se creó cuando ejecutó el comando generador; sin embargo, usted necesita configurar configuración extra. Edite el archivo para ver como lo que tengo debajo.
1 |
#app.js |
2 |
|
3 |
var express = require('express'); |
4 |
var path = require('path'); |
5 |
var favicon = require('serve-favicon'); |
6 |
var logger = require('morgan'); |
7 |
var cookieParser = require('cookie-parser'); |
8 |
var bodyParser = require('body-parser'); |
9 |
const session = require('express-session') |
10 |
const expressValidator = require('express-validator') |
11 |
const flash = require('connect-flash') |
12 |
const mongoose = require('mongoose') |
13 |
|
14 |
// 1
|
15 |
const genres = require('./routes/genres'); |
16 |
const books = require('./routes/books'); |
17 |
|
18 |
var app = express(); |
19 |
|
20 |
// 2
|
21 |
mongoose.Promise = global.Promise |
22 |
const mongoDB = process.env.MONGODB_URI || 'mongodb://127.0.0.1/tutsplus-library' |
23 |
mongoose.connect(mongoDB) |
24 |
|
25 |
// view engine setup
|
26 |
app.set('views', path.join(__dirname, 'views')); |
27 |
app.set('view engine', 'pug'); |
28 |
|
29 |
// uncomment after placing your favicon in /public
|
30 |
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
|
31 |
app.use(logger('dev')); |
32 |
app.use(bodyParser.json()); |
33 |
app.use(bodyParser.urlencoded({ extended: false })); |
34 |
app.use(cookieParser()); |
35 |
app.use(express.static(path.join(__dirname, 'public'))); |
36 |
|
37 |
// 3
|
38 |
app.use(session({ |
39 |
secret: 'secret', |
40 |
saveUninitialized: true, |
41 |
resave: true |
42 |
}))
|
43 |
|
44 |
// 4
|
45 |
app.use(expressValidator({ |
46 |
errorFormatter: function(param, msg, value) { |
47 |
var namespace = param.split('.') |
48 |
, root = namespace.shift() |
49 |
, formParam = root |
50 |
|
51 |
while(namespace.length) { |
52 |
formParam += '[' + namespace.shift() + ']' |
53 |
}
|
54 |
return { |
55 |
param : formParam, |
56 |
msg : msg, |
57 |
value : value |
58 |
}
|
59 |
}
|
60 |
}))
|
61 |
|
62 |
// 5
|
63 |
app.use(flash()) |
64 |
app.use(function (req, res, next) { |
65 |
res.locals.messages = require('express-messages') |
66 |
next() |
67 |
})
|
68 |
|
69 |
// 6
|
70 |
app.use('/genres', genres); |
71 |
app.use('/books', books); |
72 |
|
73 |
// catch 404 and forward to error handler
|
74 |
app.use(function(req, res, next) { |
75 |
var err = new Error('Not Found'); |
76 |
err.status = 404; |
77 |
next(err); |
78 |
});
|
79 |
|
80 |
// error handler
|
81 |
app.use(function(err, req, res, next) { |
82 |
// set locals, only providing error in development
|
83 |
res.locals.message = err.message; |
84 |
res.locals.error = req.app.get('env') === 'development' ? err : {}; |
85 |
|
86 |
// render the error page
|
87 |
res.status(err.status || 500); |
88 |
res.render('error'); |
89 |
});
|
90 |
|
91 |
module.exports = app; |
- Se requiere las dos rutas que va a hacer usan de en la construcción de esta aplicación. Se creará el archivo de rutas poco. Las rutas necesarias son asignadas como valores a dos variables diferentes que se utilizan al configurar el software intermedio para las rutas.
- Establece Mangosta para uso
global.Promise. La variableMongoDBse asigna laMONGODB_URIde su entorno o la ruta de acceso al servidor local de mongo. Esta variable se pasa como un argumento para conectarse al servidor de MongoDB corriente. - Configurar middleware sesión usando
express-session. Este middleware es importante ya que mostrando mensajes flash en algunas partes de su aplicación. - Configurar middleware para la validación. Este middleware se utilizará para validar el formulario de entrada, asegurando que los usuarios de la aplicación no presente una forma vacía. La validación utiliza un paquete instalado,
express-validator. - Configurar middleware que será muy útil al mostrar mensajes flash. Este middleware hace uso de
connect-flash. - Se establecen las rutas de la aplicación hasta hacer uso del archivo de rutas que usted requirió. Pide apuntando a /genres y /books hará uso de los géneros y los libros rutas de archivos respectivamente. En este momento no ha creado los archivos de rutas, pero haremos pronto.
Libro y Modelo de Género
El modelo de libro hará uso de mangosta esquema para definir cómo se estructurarán los libros. Crear un directorio llamado models y un nuevo archivo llamado Book.js. Aquí es lo que parece.
1 |
#models/Book.js |
2 |
|
3 |
const mongoose = require('mongoose') |
4 |
mongoose.Promise = global.Promise |
5 |
const Schema = mongoose.Schema |
6 |
|
7 |
const bookSchema = Schema({ |
8 |
name: { |
9 |
type: String, |
10 |
trim: true, |
11 |
required: 'Please enter a book name' |
12 |
},
|
13 |
description: { |
14 |
type: String, |
15 |
trim: true |
16 |
},
|
17 |
author: { |
18 |
type: String, |
19 |
trim: true, |
20 |
},
|
21 |
genre: [{ |
22 |
type: Schema.Types.ObjectId, |
23 |
ref: 'Genre' |
24 |
}]
|
25 |
})
|
26 |
|
27 |
module.exports = mongoose.model('Book', bookSchema) |
Aquí tienes cuatro campos. El último campo se utiliza para almacenar cada libro pertenece al género. Aquí el campo de género hace referencia al modelo de género, que se creará a continuación. Por eso el tipo se establece en Schema.Types.ObjectId, que es donde se guardarán los ID de cada género se hace referencia. Ref especifica el modelo se está haciendo referencia a. Tenga en cuenta que este género está guardado como una matriz, lo que significa que un libro puede tener más de un género.
Vamos a seguir adelante a crear el modelo de Género.
1 |
#models/genre.js |
2 |
|
3 |
const mongoose = require('mongoose') |
4 |
mongoose.Promise = global.Promise |
5 |
const Schema = mongoose.Schema |
6 |
|
7 |
const genreSchema = Schema({ |
8 |
name: { |
9 |
type: String, |
10 |
trim: true, |
11 |
required: 'Please enter a Genre name' |
12 |
}
|
13 |
})
|
14 |
|
15 |
module.exports = mongoose.model('Genre', genreSchema) |
Para su género, necesita un campo: name.
Género Indice de Ruta y Visión
Para este tutorial, usted hará uso de dos rutas de senderos para su género: un camino para añadir nuevos géneros y otro que recoge los géneros tienes. Crear un archivo en su directorio de rutas llamado genres.js.
Empezar por exigir a todos los módulos que va a hacer uso.
1 |
#routes/genres.js |
2 |
|
3 |
var express = require('express'); |
4 |
var router = express.Router(); |
5 |
const mongoose = require('mongoose') |
6 |
const Genre = require('../models/Genre') |
A continuación, caer en la ruta que controla el archivo de índice para sus géneros.
1 |
router.get('/', (req, res, next) => { |
2 |
const genres = Genre.find({}).exec() |
3 |
.then((genres) => { |
4 |
res.render('genres', { genres: genres }) |
5 |
}, (err) => { |
6 |
throw err |
7 |
})
|
8 |
});
|
Esta ruta se llama siempre que las solicitudes se realizan a /genres. Aquí se llama al método find en el modelo de género para obtener todos los géneros que se han creado. Estos géneros se procesan luego en un template llamado genres. Vamos a seguir adelante y crear que, pero en primer lugar, actualizar tu layout.pug a este aspecto:
1 |
#views/layout.pug |
2 |
|
3 |
doctype html |
4 |
html |
5 |
head |
6 |
title= title |
7 |
link(rel='stylesheet', href='/stylesheets/style.css') |
8 |
link(rel='stylesheet', href='https://bootswatch.com/paper/bootstrap.css') |
9 |
script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js') |
10 |
script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js') |
11 |
body |
12 |
.container-fluid |
13 |
block header |
14 |
nav.navbar.navbar-inverse |
15 |
.container-fluid |
16 |
.navbar-header |
17 |
button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='#bs-example-navbar-collapse-2') |
18 |
span.sr-only Toggle navigation |
19 |
span.icon-bar |
20 |
span.icon-bar |
21 |
span.icon-bar |
22 |
a.navbar-brand(href='#') Local Library |
23 |
#bs-example-navbar-collapse-2.collapse.navbar-collapse |
24 |
ul.nav.navbar-nav.navbar-right |
25 |
li |
26 |
a(href='/books') View Books |
27 |
li |
28 |
a(href='/books/add') Add New Book |
29 |
li |
30 |
a(href='/genres') View Genres |
31 |
li |
32 |
a(href='/genres/add') Add New Genre |
33 |
block content |
Esto le dará su punto de vista una buena estructura para facilitar la navegación. Ahora cree un archivo de vista llamado genre.pug. En este archivo, será recorrer los géneros creados y salida de cada género en una lista desordenada.
Aquí está cómo debe buscar el archivo.
1 |
#views/genres.pug |
2 |
|
3 |
extends layout |
4 |
|
5 |
block content |
6 |
h1 Genre |
7 |
ul.well.well-lg |
8 |
each genre, i in genres |
9 |
li.well.well-sm |
10 |
p #{genre.name}
|
Añadir Vistas y Nuevas Rutas de Género
Volver a su routes/genres.js para agregar las rutas que se encargará de la creación de nuevos géneros.
1 |
#routes/genres.js |
2 |
|
3 |
// 1
|
4 |
router.get('/add', (req, res, next) => { |
5 |
res.render('addGenre') |
6 |
})
|
7 |
|
8 |
// 2
|
9 |
router.post('/add', (req, res, next) => { |
10 |
req.checkBody('name', 'Name is required').notEmpty() |
11 |
|
12 |
const errors = req.validationErrors() |
13 |
|
14 |
if (errors) { |
15 |
console.log(errors) |
16 |
res.render('addgenres', { genre, errors }) |
17 |
}
|
18 |
|
19 |
const genre = (new Genre(req.body)).save() |
20 |
.then((data) => { |
21 |
res.redirect('/genres') |
22 |
})
|
23 |
.catch((errors) => { |
24 |
console.log('oops...') |
25 |
console.log(errors) |
26 |
})
|
27 |
})
|
28 |
|
29 |
// 3
|
30 |
module.exports = router; |
- El trabajo de este router es simplemente Mostrar la página para agregar nuevas rutas. Este router se llama siempre que las solicitudes se hacen a /genres/add camino.
- Este router encarga de la presentación del formulario. Cuando se envía el formulario, verifique que un nombre es introducido por el usuario. Si no se ha introducido ningún nombre, la página es nuevamente procesada. Si los controles son buenos ir, se guarda el género y el usuario es redirigido a la página de /genres.
- El módulo se exporta como un router.
Ahora puede seguir adelante y crear la página para agregar un nuevo género.
1 |
#views/addGenre.pug |
2 |
|
3 |
extends layout |
4 |
|
5 |
block content |
6 |
.row |
7 |
.col-md-12 |
8 |
h1 Add Book |
9 |
form(method="POST", action="/genres/add") |
10 |
.form-group |
11 |
label.col-lg-2.control.label Name |
12 |
.col-lg-10 |
13 |
input.form-control(type="text", name='name') |
14 |
.form-group |
15 |
.col-lg-10.col-lg-offset-2 |
16 |
input.button.btn.btn-primary(type='submit', value='Submit') |
17 |
|
18 |
if errors |
19 |
ul |
20 |
for error in errors |
21 |
li!= error.msg |
Libros de Rutas y Vistas
Crear un nuevo archivo de ruta para los libros y asígnele el nombre books.js. Como lo hizo antes con el género, empezar por la que requieren los módulos necesarios.
1 |
#routes/books.js |
2 |
|
3 |
var express = require('express'); |
4 |
var router = express.Router(); |
5 |
const mongoose = require('mongoose') |
6 |
const Book = require('../models/Book') |
7 |
const Genre = require('../models/Genre') |
A continuación, configurar el router para la visualización de todos los libros guardados en la biblioteca. Tratar de por su propia cuenta de la manera como se estableció del género; Usted puede comprobar siempre hacia atrás para hacer las correcciones.
Supongo que conoces – aquí es cómo debe buscar.
1 |
router.get('/', (req, res, next) => { |
2 |
const books = Book.find({}).exec().then((books) => { |
3 |
res.render('books', { books: books }) |
4 |
}, (err) => { |
5 |
throw err |
6 |
})
|
7 |
});
|
Cuando se llama a este router, se hace una solicitud para encontrar todos los libros guardados en la base de datos. Si todo va bien, los libros se muestran en la página de /books, otro que se produce un error.
Es necesario crear un nuevo archivo para la visualización de todos los libros, y aquí está cómo debe buscar.
1 |
#views/books.pug |
2 |
|
3 |
extends layout |
4 |
|
5 |
block content |
6 |
h1 Books |
7 |
ul.well.well-lg |
8 |
each book, i in books |
9 |
li.well.well-sm |
10 |
a(href=`/books/show/${book.id}`) #{book.name}
|
11 |
p= book.description |
Simplemente recorrer los libros devueltos y salida el nombre y descripción de cada libro con una lista desordenada. El nombre de los puntos de libro a la página individual del libro.
Añadir Vistas y Nuevas Rutas de Libro
El siguiente router que configurar encargará de la adición de nuevos libros. Aquí se utilizarán dos routers: uno simplemente hará que la página, y otro encargará de la presentación del formulario.
Este es el aspecto de los routers.
1 |
router.get('/add', (req, res, next) => { |
2 |
const genres = Genre.find({}).exec() |
3 |
.then((genres) => { |
4 |
res.render('addBooks', { genres }) |
5 |
})
|
6 |
.catch((err) => { |
7 |
throw err |
8 |
})
|
9 |
})
|
10 |
|
11 |
router.post('/add', (req, res, next) => { |
12 |
req.checkBody('name', 'Name is required').notEmpty() |
13 |
req.checkBody('description', 'Description is required').notEmpty() |
14 |
req.checkBody('genre', 'Genre is required').notEmpty |
15 |
|
16 |
const errors = req.validationErrors() |
17 |
|
18 |
if (errors) { |
19 |
console.log(errors) |
20 |
res.render('addBooks', { book, errors }) |
21 |
}
|
22 |
|
23 |
const book = (new Book(req.body)).save() |
24 |
.then((data) => { |
25 |
res.redirect('/books') |
26 |
})
|
27 |
.catch((errors) => { |
28 |
console.log('oops...') |
29 |
})
|
30 |
})
|
En el primer router, se muestra la página de /addBooks. Este router se llama cuando se hace una petición a /add ruta de acceso. Desde libros añadidos se suponen que tienen los géneros, que desea mostrar los géneros que se han salvado a la base de datos.
1 |
const genres = Genre.find({}).exec() |
2 |
.then((genres) => { |
El código anterior todos los géneros encuentra en la base de datos y devuelve en los variable géneros. Con esto, usted será capaz de recorrer los géneros y mostrarlos como casillas de verificación.
El segundo router encarga de la presentación del formulario. En primer lugar, se compruebe el cuerpo de la solicitud para asegurarse de que algunos campos no están vacíos. Aquí es donde el middleware express-validator que establece en app.js práctico. Si hay errores, la página es nuevamente procesada. Si no hay ninguno, la nueva instancia de libro se guarda y se redirige al usuario a la página de /books.
Vamos a ir adelante y crear los puntos de vista para esto.
Crear un nuevo archivo de vista llamado addBooks.pug. Tenga en cuenta que el nombre de la vista coincida con el primer parámetro dado a res.render. Esto es porque están haciendo una plantilla. Durante la redirección, simplemente pase la ruta de acceso que desea redirigir, como lo hizo con res.redirect('/books').
Habiendo establecido, aquí es lo que deberían ser los puntos de vista.
1 |
#views/addBooks.pug |
2 |
|
3 |
extends layout |
4 |
|
5 |
block content |
6 |
.row |
7 |
.col-md-12 |
8 |
h1 Add Book |
9 |
form(method="POST", action="/books/add") |
10 |
.form-group |
11 |
label.col-lg-2.control-label Name |
12 |
.col-lg-10 |
13 |
input.form-control(type="text", name='name') |
14 |
.form-group |
15 |
label.col-lg-2.control-label Author |
16 |
.col-lg-10 |
17 |
input.form-control(type="text", name='author') |
18 |
.form-group |
19 |
label.col-lg-2.control-label Book Description |
20 |
.col-lg-10 |
21 |
textarea#textArea.form-control(rows='3', name='description') |
22 |
.form-group |
23 |
label.col-lg-2.control-label Genre |
24 |
.col-lg-10 |
25 |
for genre in genres |
26 |
.checkbox |
27 |
input.checkbox(type='checkbox', name='genre', id=genre._id, value=genre._id, checked=genre.checked) |
28 |
label(for=genre._id) #{genre.name}
|
29 |
.form-group |
30 |
.col-lg-10.col-lg-offset-2 |
31 |
input.button.btn.btn-primary(type='submit', value='Submit') |
32 |
|
33 |
if errors |
34 |
ul |
35 |
for error in errors |
36 |
li!= error.msg |
Lo importante a notar aquí es la acción de la forma y el método. Cuando se hace clic en el botón Enviar, usted está haciendo una solicitud POST a /books/add. Otra cosa, una vez más se recorrer la colección de géneros volvió y cada uno de ellos.
Libro Ver Ruta y Visión
Nos dejó caer en la ruta para gestionar las peticiones realizadas a cada página de libros. Mientras estás allí, es importante el módulo de exportación también.
1 |
#routes/books.js |
2 |
|
3 |
router.get('/show/:id', (req, res, next) => { |
4 |
const book = Book.findById({ _id: req.params.id }) |
5 |
.populate({ |
6 |
path: 'genre', |
7 |
model: 'Genre', |
8 |
populate: { |
9 |
path: 'genre', |
10 |
model: 'Book' |
11 |
}
|
12 |
})
|
13 |
.exec() |
14 |
.then((book) => { |
15 |
res.render('book', { book }) |
16 |
})
|
17 |
.catch((err) => { |
18 |
throw err |
19 |
})
|
20 |
})
|
21 |
|
22 |
module.exports = router; |
Ninguna magia sucede aquí.
En primer lugar, las solicitudes para este router deben tener un id: el id del libro. Este identificador se obtiene de los parámetros de la petición mediante req.params.id. Esto se utiliza para identificar el libro específico que debe obtenerse de la base de datos, como los identificadores son únicos. Cuando el libro se encuentra, el valor de género del libro se rellena con todos los géneros que se han salvado a esta instancia de libro. Si todo va bien, se representa la vista de libro, que se produce un error.
Vamos a crear la vista para un libro. Aquí está cómo debe buscar.
1 |
block content |
2 |
.well.well-lg |
3 |
h1 #[strong Name:] #{book.name}
|
4 |
ul |
5 |
li #[strong Description:] #{book.description}
|
6 |
li #[strong Author]: #{book.author}
|
7 |
li #[strong Genre:] |
8 |
each genre in book.genre |
9 |
#{genre.name}
|
10 |
|, |
Usted puede iniciar su servidor nodo ejecutando:
1 |
DEBUG=tutsplus-library:* npm start |
Conclusión
Ya sabes cómo crear una aplicación web estándar en Node.js, no sólo una aplicación de tareas simples. Fuiste capaz de manejar el envío del formulario, dos modelos de referencia y establecer algunos middleware.
Puede ir más lejos mediante la extensión de la aplicación, trate de añadir la capacidad de eliminar un libro. Primero agregar un botón a la página del show, ir a los archivos de rutas y añadir un router para esto. Tenga en cuenta que esto va a ser una solicitud POST.
También se puede pensar más características para agregar a la aplicación. Espero que lo disfruté.



