En este tutorial vamos a crear un blog impulsado por Node.js en el back-end, Bootstrap en el front-end y MongoDB como almacén de datos. Vamos a empezar desde cero y construir el proyecto paso a paso. Veremos cómo incluir todos los paquetes que necesitaremos en Node para crear el blog. Algunos de estos incluyen Express, Bcrypt, nodemon, express-edge, mongoose, body-parser, express-fileupload y express-session. Comenzaremos con las rutas básicas en el archivo index.js y luego trasladaremos gradualmente la aplicación a una arquitectura Model View Controller. Empecemos.
Configuración del proyecto de blog
En nuestra terminal, podemos crear nuestro directorio y ejecutarlo npm init
para comenzar.
node $ mkdir nodejs-blog-tutorial
node $ cd nodejs-blog-tutorial
nodejs-blog-tutorial $ npm init
Esta utilidad lo guiará a través de la creación de un archivo package.json.
Solo cubre los elementos más comunes e intenta adivinar valores predeterminados razonables.
Consulte la npm help json
documentación definitiva sobre estos campos.
y exactamente lo que hacen.
Úselo luego para instalar un paquete ynpm install
guárdelo como una dependencia en el archivo package.json.
Presione ^ C en cualquier momento para salir.
nombre del paquete: (nodejs-blog-tutorial)
versión: (1.0.0)
descripción: Crea un blog usando Node.js
punto de entrada: (index.js)
comando de prueba:
repositorio git:
palabras clave: blog
autor:
licencia: (ISC)
A punto de escribir en C: nodenodejs-blog-tutorialpackage.json:
{
"nombre": "nodejs-blog-tutorial",
"versión": "1.0.0",
"description": "Crea un blog usando Node.js",
"main": "index.js",
"guiones": {
"test": "echo" Error: no se ha especificado ninguna prueba "&& exit 1"
},
"palabras clave": [
"Blog"
],
"autor": "",
"licencia": "ISC"
}
¿Esta bien? (si)
Excelente. Ahora, instalaremos un buen tema Bootstrap de la buena gente de Start Bootstrap.
nodejs-blog-tutorial $ npm i iniciobootstrap-clean-blog
npm notice creó un archivo de bloqueo como package-lock.json. Debe enviar este archivo.npm WARN bootstrap@4.1.1 requiere un par de popper.js@^1.14.3 pero ninguno está instalado. Debe instalar las dependencias de pares usted mismo.
npm WARN nodejs-blog-tutorial@1.0.0 Sin campo de repositorio.
+ startbootstrap-clean-blog@4.1.1
agregó 4 paquetes de 10 contribuidores y auditó 4 paquetes en 5.224s
encontrado 0 vulnerabilidades
Tenga en cuenta que el paquete vive en el node_modules
directorio.
También vamos a necesitar Express, así que vamos a instalarlo.
nodejs-blog-tutorial $ npm i express
npm WARN bootstrap@4.1.1 requiere un par de popper.js@^1.14.3 pero ninguno está instalado. Debe instalar las dependencias de pares usted mismo.npm WARN nodejs-blog-tutorial@1.0.0 Sin campo de repositorio.
+ express@4.16.3
agregó 50 paquetes de 47 contribuyentes y auditó 123 paquetes en 4.111s
encontrado 0 vulnerabilidades
Mientras estamos en eso, podemos instalar nodemon para la recarga en caliente de nuestros archivos JavaScript. No es necesario tener que detener y reiniciar manualmente su aplicación de nodo constantemente.
nodejs-blog-tutorial $ npm i nodemon
Ahora agreguemos el punto de entrada a nuestro proyecto de blog node.js que es index.js .
nodejs-blog-tutorial $ touch index.js
También necesitaremos tener un directorio público en el proyecto.
nodejs-blog-tutorial $ mkdir public
Comencemos por incluir express, configurar nuestro directorio público y lanzar el servidor.
index.js
| const express = require('express'); const app = new express(); app.use(express.static('public')); app.listen(4000, () => { console.log('App listening on port 4000') }); |
Construyendo una página de inicio
Para comenzar a construir la página de inicio, podemos crear un pages
directorio para almacenar archivos estáticos. Dentro de ese directorio podemos comenzar con un index.html
archivo.
nodejs-blog-tutorial $ mkdir páginas
nodejs-blog-tutorial $ cd pages
páginas $ touch index.html
No se emocione demasiado, sé que esta página de inicio es épica.
index.html
¡Vamos a mostrar esa página de inicio en el navegador ahora!
index.js
| const path = require('path'); const express = require('express'); const app = new express(); app.use(express.static('public')); app.get('/', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/index.html')); }); app.listen(4000, () => { console.log('App listening on port 4000') }); |
¡Esta funcionando!
Para que las cosas se vean mucho mejor, podemos copiar el startbootstrap-clean-blog
directorio al theme
directorio. La línea de comandos hace que esto sea rápido y fácil.
nodejs-blog-tutorial $ cp -r node_modules / tema startbootstrap-clean-blog
Tenga en cuenta que el theme
directorio tiene todo lo que necesitamos para que este blog se vea genial.
Dado que hemos establecido el public
directorio donde serviremos los activos, necesitamos copiar los directorios vendor
css
img
y js
. También copiamos el tema index.html
al pages
directorio.
nodejs-blog-tutorial $ cp -r theme / vendor public / vendor
nodejs-blog-tutorial $ cp -r theme / css public / css
nodejs-blog-tutorial $ cp -r theme / img public / img
nodejs-blog-tutorial $ cp -r theme / js public / js
nodejs-blog-tutorial $ cp -r theme / index.html pages / index.html
nodejs-blog-tutorial $ node index.js
Aplicación escuchando en el puerto 4000
Una vez que iniciamos el servidor y cargamos la página de inicio - ¡Guau! ¡Se ve bastante bien!
Agregar páginas de información, contactos y publicaciones
Primero podemos copiar about.html a nuestro pages
directorio.
nodejs-blog-tutorial $ cp -r theme / about.html pages / about.html
Ahora podemos agregar un controlador de ruta para atender las solicitudes /about
.
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const path = require('path'); const express = require('express'); const app = new express(); app.use(express.static('public')); app.get('/', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/index.html')); }); app.get('/about', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/about.html')); }); app.listen(4000, () => { console.log('App listening on port 4000') }); |
Continúe y ejecute nodemon para que el servidor se reinicie cada vez que cambiemos nuestros archivos.
nodejs-blog-tutorial $ nodemon index.js
[nodemon] 1.17.5 [nodemon] para reiniciar en cualquier momento, ingrese rs
[nodemon] mirando: *. *
[nodemon] iniciando la node index.js
aplicación escuchando en el puerto 4000
¡Aquí vamos! Una bonita página sobre.
Podemos hacer lo mismo con las páginas de contacto y publicación.
nodejs-blog-tutorial $ cp -r theme / contact.html pages / contact.html
nodejs-blog-tutorial $ cp -r theme / post.html pages / post.html
Agregue los nuevos controladores de ruta para /contact
y me /post
gusta.
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | const path = require('path'); const express = require('express'); const app = new express(); app.use(express.static('public')); app.get('/', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/index.html')); }); app.get('/about', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/about.html')); }); app.get('/contact', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/contact.html')); }); app.get('/post', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/post.html')); }); app.listen(4000, () => { console.log('App listening on port 4000') }); |
¡Bingo!
¡Excelente!
Motor de plantilla Edge con Express
Las páginas hasta ahora son estáticas. Queremos una situación más dinámica. Para esto, podemos usar el motor de plantillas Edge para usar con Express.
nodejs-blog-tutorial $ npm instalar express-edge --save
Así es como podemos especificar que ahora estamos usando el motor de plantilla Edge en index.js .
index.js
| const path = require('path'); const expressEdge = require('express-edge'); const express = require('express'); const app = new express(); app.use(express.static('public')); app.use(expressEdge); app.set('views', __dirname + '/views'); |
Ahora creemos el views
directorio que contendrá nuestros .edge
archivos.
nodejs-blog-tutorial $ mkdir vistas
nodejs-blog-tutorial $ vistas táctiles / index.edge
En index.js , continúe y elimine este código.
| app.get('/', (req, res) => { res.sendFile(path.resolve(__dirname, 'pages/index.html')); }); |
Una vez que se haya eliminado el fragmento anterior, puede colocar este código en su lugar. Esto le dice a nuestra aplicación que ahora vamos a renderizar una plantilla de borde nombrada index
desde la views
carpeta en lugar de nuestro archivo estático original.
| app.get('/', (req, res) => { res.render('index'); }); |
Diseños con Edge
Al igual que todos los demás motores de plantillas populares, podemos configurar archivos de diseño . Aquí agregamos un layouts
directorio dentro views
y agregamos un app.edge
archivo.
nodejs-blog-tutorial $ mkdir vistas / diseños
nodejs-blog-tutorial $ vistas táctiles / diseños / app.edge
En app.edge
, podemos agregar el marcado común que compartirán todas las páginas. El "medio" de la página se elimina y se reemplaza con @!section('content')
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Clean Blog - Start Bootstrap Theme</title> <!-- Bootstrap core CSS --> <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom fonts for this template --> <link href="vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"> <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link href="css/clean-blog.min.css" rel="stylesheet"> </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"> <div class="container"> <a class="navbar-brand" href="index.html">Start Bootstrap</a> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> Menu <i class="fa fa-bars"></i> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item"> <a class="nav-link" href="index.html">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="about.html">About</a> </li> <li class="nav-item"> <a class="nav-link" href="post.html">Sample Post</a> </li> <li class="nav-item"> <a class="nav-link" href="contact.html">Contact</a> </li> </ul> </div> </div> </nav> @!section('content') <!-- Footer --> <footer> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <ul class="list-inline text-center"> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-twitter fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-facebook fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-github fa-stack-1x fa-inverse"></i> </span> </a> </li> </ul> <p class="copyright text-muted">Copyright © Your Website 2018</p> </div> </div> </div> </footer> <!-- Bootstrap core JavaScript --> <script src="vendor/jquery/jquery.min.js"></script> <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script> <!-- Custom scripts for this template --> <script src="js/clean-blog.min.js"></script> </body> </html> |
Ahora realmente podemos simplificar cualquier otro archivo que extienda el archivo layout.app . Por ejemplo, ahora podemos agregar este marcado a index.edge .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Hi!</h1> <span class="subheading">Edge Layout Example</span> </div> </div> </div> </div> </header> @endsection |
Aquí está el resultado en el navegador. ¡Se ve bastante bien!
Datos dinámicos con MongoDB
Nuestro sistema de blogs Node js utilizará MongoDB para almacenar publicaciones de blog. El objetivo es poder almacenar nuevas publicaciones de blog en MongoDB, y también recuperar publicaciones de blog de MongoDB y enviar esos datos a nuestros archivos de plantilla .edge que mostrarán los datos dinámicos. Podemos empezar instalando Mongoose.
nodejs-blog-tutorial $ npm i mangosta - guardar
Ahora podemos requerir mangosta en nuestro archivo index.js y conectarnos a la base de datos como vemos aquí. No olvide la propiedad useNewUrlParser .
| const path = require('path'); const expressEdge = require('express-edge'); const express = require('express'); const mongoose = require('mongoose'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) |
Modelos de mangosta
Podemos crear un directorio dedicado para almacenar nuestros modelos Mongoose y comenzaremos con un archivo Post.js.
nodejs-blog-tutorial $ mkdir base de datos
nodejs-blog-tutorial $ cd database
base de datos $ mkdir modelos
base de datos $ modelos táctiles / Post.js
Post.js contendrá este código.
| const mongoose = require('mongoose'); const PostSchema = new mongoose.Schema({ title: String, description: String, content: String }); const Post = mongoose.model('Post', PostSchema); module.exports = Post; |
Nuevo formulario de publicación de blog
Agreguemos una página al blog donde se presenta un formulario al usuario para que pueda enviar una nueva publicación de blog.
nodejs-blog-tutorial $ vistas táctiles / create.edge
El siguiente marcado se puede colocar en el archivo create.edge .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="page-heading"> <h1>Create New Post</h1> </div> </div> </div> </div> </header> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <form action="/posts/store" method="POST"> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Title</label> <input type="text" name="title" placeholder="Title" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Description</label> <input type="text" name="description" placeholder="Description" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Content</label> <textarea name="content" placeholder="Content ..." cols="30" rows="10" class="form-control"></textarea> </div> </div> <div class="form-group my-4 text-center"> <button class="btn btn-primary">Create Post</button> </div> </form> </div> </div> </div> @endsection |
Ahora necesitamos una ruta en index.js para presentar el formulario.
| app.get('/posts/new', (req, res) => { res.render('create') }); |
Ahora podemos visitar http: // localhost: 4000 / posts / new y tenemos un formulario para crear una nueva publicación de blog.
Configuración de solicitudes POST en Express
Nuestro formulario para una nueva publicación de blog enviará una solicitud POST y necesitamos configurar el código para manejar eso. También necesitaremos el paquete body-parser para leer los datos que se envían en la solicitud POST. Primero agreguemos el analizador corporal .
nodejs-blog-tutorial $ npm instalar body-parser
Ahora necesitamos usar body-parser en index.js . Los fragmentos relevantes están resaltados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | const path = require('path'); const expressEdge = require('express-edge'); const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(express.static('public')); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })); app.get('/', (req, res) => { res.render('index'); }); app.get('/posts/new', (req, res) => { res.render('create') }); app.post('/posts/store', (req, res) => { console.log(req.body) res.redirect('/') }); |
Ahora, si ingresa algunos datos en el formulario, notará que podemos inspeccionarlos en la consola si enviamos el formulario. ¡Agradable!
[nodemon] comenzando node index.js
Aplicación escuchando en el puerto 4000
{title: 'Este es el campo del título',
descripción: 'Aquí está el campo de descripción',
contenido: 'El contenido de la publicación está aquí'}
Almacenar nueva publicación de blog en MongoDB
Ahora que estamos obteniendo los datos del formulario, tenemos que hacer algo con ellos. Queremos almacenarlo en la base de datos. Usaremos el modelo Post.js que habíamos creado anteriormente.
| const Post = require('./database/models/Post'); |
Ahora podemos actualizar el /posts/store
similar.
| app.post('/posts/store', (req, res) => { Post.create(req.body, (error, post) => { res.redirect('/') }) }); |
Eso debería ser suficiente para almacenar una nueva publicación en la base de datos. Continúe y complete el nuevo formulario de publicación de blog, luego haga clic en Enviar. Entonces podemos usar Compass para verificarlo y nuestros datos están allí.
Visualización de publicaciones de blog desde la base de datos
Ahora podemos eliminar las páginas "estáticas" que representaban publicaciones de blog de antes y usar datos dinámicos de MongoDB para mostrar publicaciones de blog. Podemos modificar la /
ruta para obtener ahora datos de MongoDB. Luego, pasaremos esos datos al archivo index.edge.
| app.get('/', async (req, res) => { const posts = await Post.find({}) res.render('index', { posts }) }); |
El archivo index.edge ahora debe arreglarse para tener en cuenta los datos dinámicos en lugar del marcado estático que teníamos antes. Usamos la @each
directiva en Edge para hacer esto. Esto nos permite recorrer varias publicaciones de blog almacenadas en la base de datos y mostrarlas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead" style="background-image: url('img/home-bg.jpg')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Clean Blog</h1> <span class="subheading">A Blog Theme by Start Bootstrap</span> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> @each(post in posts) <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> {{ post.title }} </h2> </a> </div> <hr> @endeach <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> <hr> @endsection |
Agregamos una segunda publicación de blog a la base de datos, ahora veamos la página de inicio. ¡Agradable! Ahora podemos ver el título de dos publicaciones de blog gracias a nuestro @each
bucle.
Visualización de una sola publicación de blog
Ahora que tenemos la página de inicio ordenada que puede mostrar todas las publicaciones, configuremos la capacidad de hacer clic en una sola publicación y ver su contenido. En primer lugar, necesitamos modificar la /post
ruta a lo siguiente.
| app.get('/post/:id', async (req, res) => { const post = await Post.findById(req.params.id) res.render('post', { post }) }); |
Ahora debemos hacer que se pueda hacer clic en los enlaces en el archivo index.edge para que podamos hacer clic en un título y ser llevados a la vista de publicación única. Tenga en cuenta la adición de la etiqueta de anclaje que enlaza con la identificación de publicación única de cada publicación de blog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead" style="background-image: url('img/home-bg.jpg')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Clean Blog</h1> <span class="subheading">A Blog Theme by Start Bootstrap</span> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> @each(post in posts) <div class="post-preview"> <a href="/post/{{ post._id }}"> <h2 class="post-title"> {{ post.title }} </h2> </a> </div> <hr> @endeach <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> <hr> @endsection |
Ahora, cuando pasamos el cursor sobre el enlace en la página principal, observe cómo el navegador nos muestra que está enlazando al objectid de esa publicación de blog.
Ahora actualice el archivo post.edge para mostrar la publicación del blog de forma dinámica.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-heading"> <h1>{{ post.title }}</h1> </div> </div> </div> </div> </header> <!-- Post Content --> <article> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> {{ post.content }} </div> </div> </div> </article> <hr> @endsection |
Ahora podemos hacer clic fácilmente en el título de cualquier publicación de blog y se nos lleva a esa publicación en particular para verla.
Agregar nombre de usuario y creado en a publicaciones de blog
Las publicaciones de blog deben tener un nombre de usuario asociado, así como una fecha de creación. Podemos actualizar el modelo de Post.js para reflejar esto.
| const mongoose = require('mongoose'); const PostSchema = new mongoose.Schema({ title: String, description: String, content: String, username: String, createdAt: { type: Date, default: new Date() } }); const Post = mongoose.model('Post', PostSchema); module.exports = Post; |
Necesitaremos ajustar nuestro archivo create.edge para que se adapte al nombre de usuario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="page-heading"> <h1>Create New Post</h1> </div> </div> </div> </div> </header> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <form action="/posts/store" method="POST"> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Username</label> <input type="text" name="username" placeholder="Username" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Title</label> <input type="text" name="title" placeholder="Title" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Description</label> <input type="text" name="description" placeholder="Description" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Content</label> <textarea name="content" placeholder="Content ..." cols="30" rows="10" class="form-control"></textarea> </div> </div> <div class="form-group my-4 text-center"> <button class="btn btn-primary">Create Post</button> </div> </form> </div> </div> </div> @endsection |
Antes de agregar nuevas publicaciones de blog, continúe y elimine la base de datos de Mongo usando el shell de línea de comando.
> mongo
> usar node-blog
cambió a db node-blog
> db.dropDatabase ()
{"drop": "node-blog", "ok": 1}
>
Ahora podemos agregar algunas publicaciones de blog nuevas a una nueva base de datos visitando http: // localhost: 4000 / posts / new y si marcamos Compass podemos ver las nuevas propiedades en nuestra base de datos. Observe las propiedades createdAt
y username
.
Podemos actualizar index.edge para que podamos mostrar estos nuevos datos ahora.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead" style="background-image: url('img/home-bg.jpg')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Clean Blog</h1> <span class="subheading">A Blog Theme by Start Bootstrap</span> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> @each(post in posts) <div class="post-preview"> <a href="/post/{{ post._id }}"> <h2 class="post-title"> {{ post.title }} </h2> </a> <p class="post-meta">Posted by <a href="#">{{ post.username }}</a> on {{ post.createdAt.toDateString() }} </p> </div> <hr> @endeach <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> <hr> @endsection |
Visitar la página de inicio del blog muestra que estas nuevas propiedades están funcionando muy bien.
Asegurémonos de que post.edge ahora también está aprovechando la información post-meta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-heading"> <h1>{{ post.title }}</h1> <span class="meta">Posted by <a href="#">{{ post.username }}</a> on {{ post.createdAt.toDateString() }} </span> </div> </div> </div> </div> </header> <!-- Post Content --> <article> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> {{ post.content }} </div> </div> </div> </article> <hr> @endsection |
¡Se ve bien!
Cómo subir imágenes
Agreguemos la capacidad de cargar imágenes al crear una nueva publicación de blog. Podemos usar el paquete express-fileupload para ayudarnos.
nodejs-blog-tutorial $ npm install --save express-fileupload
Podemos agregar un campo para agregar una nueva imagen al crear una publicación en create.edge .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="page-heading"> <h1>Create New Post</h1> </div> </div> </div> </div> </header> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <form action="/posts/store" method="POST" encType="multipart/form-data"> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Username</label> <input type="text" name="username" placeholder="Username" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Title</label> <input type="text" name="title" placeholder="Title" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Description</label> <input type="text" name="description" placeholder="Description" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Content</label> <textarea name="content" placeholder="Content ..." cols="30" rows="10" class="form-control"></textarea> </div> </div> <div class="form-group mt-3"> <input type="file" name="image" class="form-control-file"> </div> <div class="form-group my-4 text-center"> <button class="btn btn-primary">Create Post</button> </div> </form> </div> </div> </div> @endsection |
Necesitamos actualizar el modelo Post.js para permitir nuestra nueva imagen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const mongoose = require('mongoose') const PostSchema = new mongoose.Schema({ title: String, description: String, content: String, username: String, image: String, createdAt: { type: Date, default: new Date() } }) const Post = mongoose.model('Post', PostSchema) module.exports = Post |
Necesitamos hacer algunas actualizaciones en index.js también para manejar la carga de imágenes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | const path = require('path'); const expressEdge = require('express-edge'); const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const fileUpload = require("express-fileupload"); const Post = require('./database/models/Post'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static('public')); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })); app.get('/', async (req, res) => { const posts = await Post.find({}) res.render('index', { posts }) }); app.get('/posts/new', (req, res) => { res.render('create') }); app.post("/posts/store", (req, res) => { const { image } = req.files image.mv(path.resolve(__dirname, 'public/posts', image.name), (error) => { Post.create({ ...req.body, image: `/posts/${image.name}` }, (error, post) => { res.redirect('/'); }); }) }); app.get('/about', (req, res) => { res.render('about'); }); app.get('/contact', (req, res) => { res.render('contact'); }); app.get('/post/:id', async (req, res) => { const post = await Post.findById(req.params.id) res.render('post', { post }) }); app.listen(4000, () => { console.log('App listening on port 4000') }); |
También necesitamos un directorio para guardar las imágenes.
nodejs-blog-tutorial $ mkdir public / posts
Ahora, si creamos una nueva publicación e incluimos una imagen, se almacena en este directorio.
Ahora podemos mostrar esa imagen en cada publicación que creamos actualizando post.edge .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead" style="background-image: url('{{ post.image }}')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-heading"> <h1>{{ post.title }}</h1> <span class="meta">Posted by <a href="#">{{ post.username }}</a> on {{ post.createdAt.toDateString() }} </span> </div> </div> </div> </div> </header> <!-- Post Content --> <article> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> {{ post.content }} </div> </div> </div> </article> <hr> @endsection |
Al ver esta publicación ahora, se ve muy bien con la imagen de fondo configurada dinámicamente.
Agregar validación simple a la creación de publicaciones de blog
Todavía no tenemos ninguna forma de validar los datos antes de intentar enviar una publicación. Agreguemos un middleware para ayudar con esto.
nodejs-blog-tutorial $ mkdir middleware
nodejs-blog-tutorial $ touch middleware / storePost.js
En el archivo storePost.js , podemos agregar la validación más básica. Básicamente, solo queremos que todos los campos sean obligatorios. Si falta alguno, simplemente lo redireccionamos al formulario.
| module.exports = (req, res, next) => { if (!req.files.image || !req.body.username || !req.body.title || !req.body.description || !req.body.content) { return res.redirect('/posts/new') } next() } |
Ahora, usemos ese middleware en index.js .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | const path = require('path'); const expressEdge = require('express-edge'); const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const fileUpload = require("express-fileupload"); const Post = require('./database/models/Post'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static('public')); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get('/', async (req, res) => { const posts = await Post.find({}) res.render('index', { posts }) }); app.get('/posts/new', (req, res) => { res.render('create') }); app.post("/posts/store", (req, res) => { const { image } = req.files image.mv(path.resolve(__dirname, 'public/posts', image.name), (error) => { Post.create({ ...req.body, image: `/posts/${image.name}` }, (error, post) => { res.redirect('/'); }); }) }); app.get('/about', (req, res) => { res.render('about'); }); app.get('/contact', (req, res) => { res.render('contact'); }); app.get('/post/:id', async (req, res) => { const post = await Post.findById(req.params.id) res.render('post', { post }) }); app.listen(4000, () => { console.log('App listening on port 4000') }); |
¡Excelente! La validación simple ahora está funcionando.
Agregar controladores para una aplicación de controlador de vista de modelo
¿Ha notado que el archivo index.js está comenzando a hincharse mucho? Esto no es realmente ideal. Podemos solucionar esto reestructurando la aplicación a una arquitectura MVC. Podemos crear el directorio de controladores y los archivos de controlador que necesitamos ahora.
nodejs-blog-tutorial $ mkdir controllers
nodejs-blog-tutorial $ touch controllers / createPost.js
nodejs-blog-tutorial $ touch controllers / getPost.js
nodejs-blog-tutorial $ touch controllers / homePage.js
nodejs-blog-tutorial $ touch controllers / storePost.js
En esos archivos estará este código.
createPost.js
| module.exports = (req, res) => { res.render("create"); }; |
getPost.js
| const Post = require('../database/models/Post') module.exports = async (req, res) => { const post = await Post.findById(req.params.id); res.render("post", { post }); } |
homePage.js
| const Post = require('../database/models/Post') module.exports = async (req, res) => { const posts = await Post.find({}); res.render("index", { posts }); } |
storePost.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const path = require('path') const Post = require('../database/models/Post') module.exports = (req, res) => { const { image } = req.files image.mv(path.resolve(__dirname, '..', 'public/posts', image.name), (error) => { Post.create({ ...req.body, image: `/posts/${image.name}` }, (error, post) => { res.redirect("/"); }); }) } |
Con nuestros controladores en su lugar, realmente podemos simplificar index.js ahora:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const createPostController = require('./controllers/createPost') const homePageController = require('./controllers/homePage') const storePostController = require('./controllers/storePost') const getPostController = require('./controllers/getPost') const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
¡Okay! Con MVC ahora en su lugar, ¿la aplicación todavía funciona? Creemos una nueva publicación de blog sobre Super Mario Bros.
¡Navegar a esa nueva publicación de blog nos muestra que todo sigue funcionando muy bien!
Agregar registro de usuario
Agreguemos la posibilidad de que un usuario se registre en el sitio para que pueda publicar blogs. Primero podemos crear un nuevo archivo de vista para eso.
nodejs-blog-tutorial $ vistas táctiles / register.edge
En register.edge podemos agregar este marcado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="page-heading"> <h1>Create A New Account</h1> </div> </div> </div> </div> </header> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <form action="/users/register" method="POST" encType="multipart/form-data"> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Username</label> <input type="text" name="username" placeholder="Username" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Email</label> <input type="email" name="email" placeholder="Email" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Password</label> <input type="password" name="password" placeholder="Password" class="form-control"> </div> </div> <div class="form-group my-4 text-center"> <button class="btn btn-primary">Register now</button> </div> </form> </div> </div> </div> @endsection |
Ahora agreguemos un controlador createUser.js y agreguemos el código que necesitamos.
nodejs-blog-tutorial $ touch controllers / createUser.js
| module.exports = (req, res) => { res.render('register') } |
Finalmente, podemos actualizar index.js así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.get("/auth/register", createUserController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
Ahora necesitamos un modelo User.js para manejar usuarios.
nodejs-blog-tutorial $ touch database / models / User.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | const bcrypt = require('bcrypt') const mongoose = require('mongoose') const UserSchema = new mongoose.Schema({ username: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true } }) UserSchema.pre('save', function (next) { const user = this bcrypt.hash(user.password, 10, function (error, encrypted) { user.password = encrypted next() }) }) module.exports = mongoose.model('User', UserSchema) |
También necesitamos un controlador storeUser.js .
nodejs-blog-tutorial $ touch controllers / storeUser.js
| const User = require('../database/models/User') module.exports = (req, res) => { User.create(req.body, (error, user) => { if (error) { return res.redirect('/auth/register') } res.redirect('/') }) } |
Ahora podemos hacer referencia a este nuevo controlador en index.js .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.get("/auth/register", createUserController); app.post("/users/register", storeUserController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
Bien, con todo en su lugar, intentemos crear un nuevo usuario.
Ahora podemos usar Mongo Compass para verificar y ver si nuestro nuevo usuario está allí. De hecho, el usuario está ahí, por lo que parece que está funcionando muy bien.
Configuración de inicio de sesión de usuario
Dado que los usuarios ahora pueden registrarse, también debemos darles la posibilidad de iniciar sesión. Primero hagamos un archivo login.edge .
nodejs-blog-tutorial $ vistas táctiles / login.edge
Podemos agregar este marcado a login.edge.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | @layout('layouts.app') @section('content') <!-- Page Header --> <header class="masthead"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="page-heading"> <h1>Login</h1> </div> </div> </div> </div> </header> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <form action="/users/login" method="POST" encType="multipart/form-data"> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Email</label> <input type="email" name="email" placeholder="Email" class="form-control"> </div> </div> <div class="control-group"> <div class="form-group floating-label-form-group controls"> <label>Password</label> <input type="password" name="password" placeholder="Password" class="form-control"> </div> </div> <div class="form-group my-4 text-center"> <button class="btn btn-primary">Login</button> </div> </form> </div> </div> </div> @endsection |
Ahora podemos crear el archivo de controlador login.js .
nodejs-blog-tutorial $ touch controllers / login.js
Aquí está el código para login.js .
| module.exports = (req, res) => { res.render('login') } |
Hagamos uso de ese nuevo loginController en index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const loginController = require("./controllers/login"); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.get('/auth/login', loginController); app.get("/auth/register", createUserController); app.post("/users/register", storeUserController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
¡Así tenemos una bonita página de inicio de sesión ahora!
Manejo de la solicitud POST de inicio de sesión
La sección anterior nos permite presentar un formulario al usuario para que pueda ingresar sus credenciales. Ahora, cuando hacen clic en el botón para iniciar sesión, necesitamos tener el código en su lugar para manejar esa solicitud POST. Primero, crearemos un controlador loginUser.js y agregaremos un código.
nodejs-blog-tutorial $ touch controllers / loginUser.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | const bcrypt = require('bcrypt') const User = require('../database/models/User') module.exports = (req, res) => { const { email, password } = req.body; // try to find the user User.findOne({ email }, (error, user) => { if (user) { // compare passwords. bcrypt.compare(password, user.password, (error, same) => { if (same) { // store user session. res.redirect('/') } else { res.redirect('/auth/login') } }) } else { return res.redirect('/auth/login') } }) } |
Una vez más, necesitamos usar este controlador en index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const loginController = require("./controllers/login"); const loginUserController = require('./controllers/loginUser'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)) app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.get('/auth/login', loginController); app.post('/users/login', loginUserController); app.get("/auth/register", createUserController); app.post("/users/register", storeUserController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
Persistencia de inicios de sesión mediante sesiones
Toda la lógica anterior funciona muy bien, pero el usuario no persiste en una sesión todavía, solucionaremos eso ahora instalando express-session.
nodejs-blog-tutorial $ npm instalar sesión rápida
Luego, agregue el express-session
paquete a index.js .
| const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const expressSession = require('express-session'); |
También queremos usar ese paquete así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const expressSession = require('express-session'); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const loginController = require("./controllers/login"); const loginUserController = require('./controllers/loginUser'); const app = new express(); app.use(expressSession({ secret: 'secret' })); |
Necesitamos ajustar el controlador loginUser.js para almacenar la sesión del usuario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | const bcrypt = require('bcrypt') const User = require('../database/models/User') module.exports = (req, res) => { const { email, password } = req.body; // try to find the user User.findOne({ email }, (error, user) => { if (user) { // compare passwords. bcrypt.compare(password, user.password, (error, same) => { if (same) { req.session.userId = user._id res.redirect('/') } else { res.redirect('/auth/login') } }) } else { return res.redirect('/auth/login') } }) } |
Con nuestra sesión ahora en su lugar, podemos poner una marca en nuestro controlador createPost.js . Si el usuario no ha iniciado sesión, podemos redirigirlo a la página de inicio de sesión. Solo mostraremos la página "Crear nueva publicación" a un usuario que haya iniciado sesión.
| module.exports = (req, res) => { if (req.session.userId) { return res.render("create"); } res.redirect('/auth/login') }; |
Almacenamiento de sesiones en MongoDB
Podemos usar el paquete connect-mongo para habilitar la capacidad de almacenar sesiones en la base de datos. Hagámoslo ahora.
nodejs-blog-tutorial $ npm me conecto-mongo
Una vez instalado, podemos actualizar index.js así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | const expressEdge = require("express-edge"); const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const expressSession = require('express-session'); const connectMongo = require('connect-mongo'); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const loginController = require("./controllers/login"); const loginUserController = require('./controllers/loginUser'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)); const mongoStore = connectMongo(expressSession); app.use(expressSession({ secret: 'secret', store: new mongoStore({ mongooseConnection: mongoose.connection }) })); app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const storePost = require('./middleware/storePost') app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", createPostController); app.post("/posts/store", storePostController); app.get('/auth/login', loginController); app.post('/users/login', loginUserController); app.get("/auth/register", createUserController); app.post("/users/register", storeUserController); app.listen(4000, () => { console.log("App listening on port 4000"); }); |
Ahora, cuando iniciamos sesión, la información de la sesión se almacena en MongoDB. De hecho, cuando revisamos nuestra base de datos en MongoDB usando Compass, podemos ver esa nueva colección de sesiones.
Configurar middleware de autenticación
Podemos proteger varias páginas o rutas usando middleware. Vamos a crear auth.js y añadir el código necesario.
nodejs-blog-tutorial $ touch middleware / auth.js
| const User = require('../database/models/User') module.exports = (req, res, next) => { User.findById(req.session.userId, (error, user) => { if (error || !user) { return res.redirect('/') } next() }) } |
Ahora puede importar ese módulo a index.js y pasarlo como segundo parámetro a app.get (“/ posts / new”, createPostController).
| const auth = require("./middleware/auth"); app.use('/posts/store', storePost) app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", auth, createPostController); app.post("/posts/store", storePostController); app.get('/auth/login', loginController); app.post('/users/login', loginUserController); app.get("/auth/register", createUserController); app.post("/users/register", storeUserController); |
Mensajes flash con connect-flash
Veamos si podemos configurar mensajes flash para cuando un usuario intenta enviar un formulario con errores. Usaremos el paquete connect-flash para hacer esto. Primero lo instalamos.
nodejs-blog-tutorial $ npm i conecto-flash
Importe a index.js usando:
| const connectFlash = require("connect-flash"); |
Luego regístrelo en index.js así:
Con connect-flash
en su lugar, ahora podemos usarlo en storeUser.js controlador .
| const User = require('../database/models/User') module.exports = (req, res) => { User.create(req.body, (error, user) => { if (error) { const registrationErrors = Object.keys(error.errors).map(key => error.errors[key].message) req.flash('registrationErrors', registrationErrors) return res.redirect('/auth/register') } res.redirect('/') }) } |
El controlador createUser.js también deberá actualizarse.
| module.exports = (req, res) => { res.render('register', { errors: req.flash('registrationErrors') }) } |
Por último, para mostrar estos mensajes flash, podemos actualizar register.edge agregando este fragmento justo encima del formulario.
| @if(errors.length > 0) <ul class="list-group"> @each(error in errors) <li class="list-group-item text-danger">{{ error }}</li> @endeach </ul> @endif |
Ahora, si un usuario comete un error durante el proceso de registro, los errores se mostrarán solo una vez.
Impedir que los usuarios autenticados visiten las páginas de registro o de inicio de sesión
Una vez que un usuario ha iniciado sesión, ya no necesita ver las páginas de registro o inicio de sesión. Podemos aplicar un middleware para hacer cumplir esto.
nodejs-blog-tutorial $ touch middleware / redirectIfAuthenticated.js
| const User = require('../database/models/User') module.exports = (req, res, next) => { if (req.session.userId) { return res.redirect('/') } next() } |
Ahora, todo el middleware en index.js se puede actualizar así.
| const storePost = require('./middleware/storePost'); const auth = require("./middleware/auth"); const redirectIfAuthenticated = require('./middleware/redirectIfAuthenticated') app.get("/", homePageController); app.get("/post/:id", getPostController); app.get("/posts/new", auth, createPostController); app.post("/posts/store", auth, storePost, storePostController); app.get("/auth/login", redirectIfAuthenticated, loginController); app.post("/users/login", redirectIfAuthenticated, loginUserController); app.get("/auth/register", redirectIfAuthenticated, createUserController); app.post("/users/register", redirectIfAuthenticated, storeUserController); |
Mostrar solo enlaces de inicio de sesión y registro para invitados
También necesitamos una forma de mostrar condicionalmente los enlaces de inicio de sesión y registro en función de si el usuario está conectado o no. Primero, actualice index.js así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | const expressEdge = require("express-edge"); const express = require("express"); const edge = require("edge.js"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const fileUpload = require("express-fileupload"); const expressSession = require('express-session'); const connectMongo = require('connect-mongo'); const connectFlash = require("connect-flash"); const createPostController = require('./controllers/createPost'); const homePageController = require('./controllers/homePage'); const storePostController = require('./controllers/storePost'); const getPostController = require('./controllers/getPost'); const createUserController = require("./controllers/createUser"); const storeUserController = require('./controllers/storeUser'); const loginController = require("./controllers/login"); const loginUserController = require('./controllers/loginUser'); const app = new express(); mongoose.connect('mongodb://localhost:27017/node-blog', { useNewUrlParser: true }) .then(() => 'You are now connected to Mongo!') .catch(err => console.error('Something went wrong', err)); app.use(connectFlash()); const mongoStore = connectMongo(expressSession); app.use(expressSession({ secret: 'secret', store: new mongoStore({ mongooseConnection: mongoose.connection }) })); app.use(fileUpload()); app.use(express.static("public")); app.use(expressEdge); app.set('views', __dirname + '/views'); app.use('*', (req, res, next) => { edge.global('auth', req.session.userId) next() }); |
En app.edge , solo mostramos los enlaces de inicio de sesión y registro si el usuario no ha iniciado sesión.
| @if(!auth) <li class="nav-item"> <a class="nav-link" href="/auth/login">Login</a> </li> <li class="nav-item"> <a class="nav-link" href="/auth/register">Register</a> </li> @endif |
Ahora, un invitado verá los enlaces de registro e inicio de sesión.
El usuario que ha iniciado sesión no ve esos enlaces.
Saliendo de tu cuenta
En primer lugar, podemos actualizar el área de navegación en app.edge así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <ul class="navbar-nav ml-auto"> <li class="nav-item"> <a class="nav-link" href="/">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="/posts/new">New Post</a> </li> @if(!auth) <li class="nav-item"> <a class="nav-link" href="/auth/login">Login</a> </li> <li class="nav-item"> <a class="nav-link" href="/auth/register">Register</a> </li> @else <li class="nav-item"> <a class="nav-link" href="/auth/logout">Logout</a> </li> @endif </ul> |
Agregue esta ruta a index.js .
| app.get("/auth/logout", redirectIfAuthenticated, logoutController); |
Cree el controlador logout.js y agregue el código necesario.
nodejs-blog-tutorial $ touch controllers / logout.js
| module.exports = (req, res) => { req.session.destroy(() => { res.redirect('/') }) } |
Asegúrese de importar el controlador de cierre de sesión como tal.
| const logoutController = require("./controllers/logout"); |
Esto permitirá al usuario cerrar sesión fácilmente en la aplicación.
Resumen del tutorial del blog de Node.js
Eso lo resume todo para este tutorial sobre cómo crear un blog desde cero usando Node.js y varios paquetes del ecosistema NPM. ¡Eso es para comprobarlo!
0 Comentarios