In order to experience the content of front-end and back-end interaction, I found art template + Express + mongodb( Code GitHub - > https://github.com/molifenge/selfblog )This simple small project practice hands, the page is extremely simple, do not like to spray. This project is mainly developed by using the front-end engine art template + Express + mongodb. This article will explain the technology and some details of the project.
1, RenderingsFront interface: article list page + article details page
1. Front page (including paging function)
2. Realization of article details page + comment function
Background interface: login page + user list page + article list page
1. User login:
2. User add delete (paging function)
3. User modification function (unable to modify if password comparison fails)
4. Article adding function (including picture upload)
5. Article modification and deletion function
Function completed
- User login
- Add, modify and delete users (including password verification and data paging functions)
- Article addition, modification and deletion (including picture upload and data paging functions)
- Front page display of article list
- Article details page
- Article comment function
Looking forward to optimization or realization
- User registration function
- When the article is modified, the original date is displayed by default
- The implementation of adaptive layout of front page list
- Front desk article list page beautification
- Comment function editor to Markdown editor
- Front end engine art template + express art template
- Express
- MongoDB
- Body parser plug-in (processing post parameter)
- Formable plug-in (parsing uploaded file data)
- bcrypt plug-in (encrypt password)
- Mongoose sex page module (implementation of paging function)
- Joi plug in (data validation)
- Express session (used to store the sessionid of the login user)
Use of Express
4.1 Express
Express is a web application development framework based on Node platform. It provides a series of powerful features to help us create various web applications.
First, we need to install Express.
npm i express
Then, we use express to create a web server for later page interaction:
//Introducing the express framework const express = require('express'); //Create web server const app = express(); //Listen to port 8080, i.e. you can access the files in this project through localhost:8080 / app.listen(8080):
4.2 Express Middleware
Middleware is a bunch of methods. Node provides middleware that can intercept requests, respond to them, or deliver them to the next middleware.
Here is a schematic diagram of middleware I have seen, which is very clear:
Middleware consists of two parts: middleware method and request processing function. Middleware methods are provided by Express to intercept requests; request processing functions are written by programmers to process requests.
For example, app.use(path,function(req,res,next)). The first parameter path is the path to be requested, and the second parameter is the corresponding request processing function. This function includes three parameters, namely req, res and next. Req is a request; res is a response; next is a method, because we may use more than one Middleware in the program. The next method is used to release the request and jump to the next middleware. This parameter is written when it is needed. If it is not used, it can not be written.
In this project, I mainly used several middleware: app.use(), app.get(), app.port(), and the built-in middleware express.static(), which have different functions.
app.use(path,function(req,res,next)) -- when a request is made to the page path (no matter what request is), call the latter request processing function.
app.get(path,function(req,res,next)) -- when a get request is sent to the page path, the next request processing function is called.
app.port(path,function(req,res,next)) -- when a get request (form submission data) is sent to the page path, the later request processing function is called.
App. Use (express. Static (path. Join ('dirname, 'public))) -- open static resource file, so you can use url to access static resources such as pictures and css in the project (public page is used to store static resources).
4.3 routing documents in the project
In the project, I put all the front-end pages in the views folder, and all the routing processing logic in the route folder. Then, according to the current background of the item, further distinguish the two folders admin (background) and home (foreground).
In order to make the dependency between files more clear, all the routing processing functions are encapsulated into a routing processing module, and then the whole module is substituted. According to the front and back functions, the routing processing is divided into two main files. admin.js is responsible for processing the request for the back system page, and home.js is responsible for the request for the front page. So write in app.js as follows:
//Import two main routing processing logic const admin = require('./route/admin'); const home = require('./route/home'); //When accessing localhost:8080/admin, a request will be sent to admin. The same as above for home app.use('/admin',admin); app.use('/home',home);
Then, in admin.js, create a route through express.Router(), and then use middleware to intercept page requests and corresponding route processing modules. Finally, don't forget to export an entire module, module.exports = admin. The file reads:
// Back-stage management //Reference express framework const express = require('express'); //Create a blog presentation page route const admin = express.Router(); //Render login page admin.get('/login',require('./admin/loginPage')); //Receive and judge the data of login page admin.post('/login',require('./admin/login')); //Create user list route admin.get('/user',require('./admin/userPage')); //Implement exit function admin.get('/logout',require('./admin/logout')); // 1. User management // Create user edit page route admin.get('/user-edit',require('./admin/user-edit')); //Create a route to implement user add function admin.post('/user-edit',require('./admin/user-edit-fn')); // User modify route admin.post('/user-modify',require('./admin/user-modify')); // Delete user function route admin.get('/delete',require('./admin/delete')); // 2. Article management // Article list page routing admin.get('/article',require('./admin/article')); // Article editing page routing admin.get('/article-edit',require('./admin/article-edit')); // Article add function route admin.post('/article-edit',require('./admin/article-add')); //User modify route admin.post('/article-modify',require('./admin/article-modify')); // Delete article feature route admin.get('/article-delete',require('./admin/article-delete')); module.exports = admin;
home.js is not listed here. You can go to Github to check it.
Use of MongoDB
4.5 MongoDB
MongoDB is a database, which has many advantages. One of them is that MongoDB does not need to explicitly create a database. If the database being used does not exist, MongoDB will automatically create it.
Mongoose, a third-party module, is required to operate MongoDB. Many functions to be used (including normal addition, deletion, query and modification) are in mongoose.
First, we need to install MongoDB. At the command line, enter:
npm i mongoose
Then start the database:
net start mongodb
To stop a database connection:
net stop mongodb
Finally, connect to the database.
const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/playground') .then(() => console.log('Database connection successful')) .catch(err => console.log('Database connection failed',err));
be careful!!! MongoDB has no account and password by default. If you want to know how to access the database with an account and password, give directions – > MongoDB sets the user name and password https://www.jianshu.com/p/237a0c5ad9fa.
4.5 database tables used in the project
I put the logic of creating database tables and connecting databases required by this project in the model folder. According to the requirements, this project needs to build three tables, namely user, article and comment, corresponding to user.js, article.js and comment.js. The corresponding fields can be viewed directly in the code.
4.6 creation set, addition, deletion, query and modification of mongodb
4.6.1 create setThere are two steps to create a set: 1. Set rules for the set; 2. Create a set.
Take user.js as an example:
//1. Create a set rule const UserSchema = new mongoose.Shema({ username:{ type:String,//Type - value type required:true,//require - true is required minlength:2,//Minimum string length maxlength:20//Maximum string length },//User name email:{ type:String, // unique is true to ensure that the email address is not duplicate, because this is used as the login name unique:true, required:true },//mailbox password:{ type:String, required:true },//Password role:{ type:String, required:true },//Role, admin is super administrator, normal is normal user state:{ type:Number, default:0//The default value is 0 }//Status, 0 -- enabled, 1 -- disabled }); //2. Create set (table) User const User = mongoose.model('User',userSchema); //Finally, remember to export the collection, otherwise other routing modules cannot use it module.exports = User;
Pay attention here!! After the collection is created, we don't necessarily see it in the database. A collection can only be created explicitly if it has data in it.
4.6.2 add, delete, check and modifyThe following are some database addition, deletion, query and modification statements used in the project:
function createUser(){ //Add a document. const salt = await bcrypt.genSalt(); const pass = await bcrypt.hash('123456',salt); const user = await User.create({ username:'iteheima', email:'[email protected]', password:pass, role:'admin', state:0 }); User.find().then(result => console.log(result)); //Query all documents in User User.findOne({_id:id}); //Query the document whose id value is id (condition) in the User table. User.findOneAndDelete({_id:id}); //Delete a document with id User.updateOne({_id:id},{ //Update the document whose id value is id. see the second parameter for the updated data username:username, email:email, role:role, state:state });5, Art template
Front end rendering is to render data to front-end pages. Generally speaking, there are three methods:
- JS primitive syntax rendering
- Front end rendering engines such as art template
- Vue template syntax.
Here I use the art template template engine.
Art template is a simple and super fast template engine. It uses scope pre declaration technology to optimize the template rendering speed, so as to achieve the running performance close to the JavaScript limit, and supports both NodeJS and browsers.
First, install art template. In order to better support the use of art template in Express, I also installed Express art template.
npm i art-template express-art-template
Then, make some configuration:
//Import art template const template = require('art-template'); const path = require('path'); //Tells the browser which template engine to use when rendering a module with an art suffix app.engine('art',require('express-art-template')); //Tell the express framework where the template is located app.set('views',path.join(__dirname,'views')); //Tell the default suffix of the express framework template app.set('view engine','art');
In this way, we can use the art template template syntax for front-end rendering. Next, we will talk about the application of art template in this project.
First of all, you should know that there are some common modules in the project, such as the head, tail, navigation bar and so on. In order to reduce code redundancy and the amount of code we write, art template provides two methods: template inheritance and sub template application. Then I put some public modules in the corresponding common folder.
The common folder directory is as follows:
layout.art: page skeleton, used to place common parts of the page, such as some files such as < HTML >, < head >, < body >.
<!-- Template inheritance --> <!-- layout.art-The page skeleton is used to place the tags and resources used by all pages, including some<html>,<head>,<body>Etc. fixed label--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <title>Blog - Content Manager</title> <link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/admin/css/base.css"> <!--this block Import the corresponding template's link Part--> {}{{/block}} </head> <body> <!--this block Import the corresponding template's main Section, that is, the main part of the page--> {} {{/block}} <script src="/admin/lib/jquery/dist/jquery.min.js"></script> <script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script> <script src="/admin/js/common.js"></script> <!--this block Import the corresponding template's script Part, i.e. calculation logic part--> {} {{/block}} </body> </html>
header.art and aside.art: the common part of the page, which is the header and side navigation bar.
Then, inherit layout.art from the corresponding page and insert the sub templates header.art and aside.art.
For example, user.art (see the code for details):
<!--inherit layout.art,Soon to each block Put it in the corresponding layout.art Corresponding position--> {} {} <!-- The relative path of the sub template is the current file because it is parsed by the template engine instead of the browser --> <!--Insert common part sub template--> {} <!-- Main content --> <div> {} <div> <!-- Classified headings --> <div> <h4>user{}</h4> <span>find{}Individual users</span> <a href="/admin/user-edit">New users</a> </div> <!-- /Classified headings --> <!-- Content list --> <table> ... </table> <!-- /Content list --> <!-- paging --> <ul> ... </ul> <!-- /paging --> </div> </div> <!-- /Main content --> <!-- Delete confirmation pop-up --> <div> ... </div> {{/block}} {} <script type="text/javascript"> $('.delete').on('click', function () { // Get user id var id = $(this).attr('data-id'); // Store the user id to be deleted in the hidden domain $('#deleteUserId').val(id); }) </script> {{/block}}6, Related plug-ins
6.1 body-parser
Body parser is also an Express middleware, which is mainly used to parse the common request parameters submitted by post.
Its use is as follows:
//Introducing body parser module const bodyParser = require('body-parser'); //Configure the body parser module, remember to write it in front of all Middleware app.use(bodyParser.urlencoded());
In this way, you can access the data submitted by the form in req.body after the post submits the request.
However, the body parser can only parse ordinary form data. If there is file upload function to the project, the body parser cannot parse, and req.body will be an empty object. So we need to use the formatible third-party module.
6.2 formidable
The formatible third-party module is also used to parse forms. It supports get, post request parameters, and file upload functions.
First, install formatible, and enter:
npm i formidable
Then, in the routing module of the form data to be uploaded using the file:
//1. Introduce formatible const formidable = require('formidable'); //2. Create a form resolution object const form = new formidable.IncomingForm(); //3. Set file upload path form.uploadDir = path.join(__dirname,'../','../','public','uploads'); //4. Keep the extension of the form upload file form.keepExtensions = true; //5. Parsing form form.parse(req,async (err,fields,files) => { //Fields are used to access common form objects, such as fields.title //files is used to access the uploaded file. files.cover.path is the absolute path of the uploaded file ... });
Read file:
//1. Create file read object var reader = new FileReader(); //2. Read file reader.readAsDataURL(this.files[0]); reader.onload = funtion(){ //Save the file read results in the page for display preview.ssrc = reader.result; }
6.3 bcypt
Bcypt is used to encrypt the password. The password in the database was originally stored in plaintext, which is extremely insecure. So we use bcypt to encrypt the password and store it in the database.
First, install bcypt, and on the command line, type:
npm i bcyptjs
In our project, the place to use password encryption is to add users. When we fill in the information of users to be added in the form and click Submit, the form data will be saved in req.body and passed to the corresponding routing processing module. In the routing module, we replace the encrypted password with req.body.password and save it in the database later.
See / route/user-edit-fn.js:
//1. Import bcypt const bcypt = require('bcryptjs'); //2. Generate random string const salt = bcrypt.genSalt(10); //3. Use random string to encrypt password const password = bcypt.hash(req.body.password,salt); //Replace the encrypted password with req.body.password req.body.password = password;
In the login page, there are also places where bcrpt is used: after we fill in the account password, we need to compare the password. At this time, call the compare method in bcypt to compare the entered password req.body.password with the password password in the database:
const bcrypt = rquire('bcrypt'); const password = req.body.password; let isValid = bcypt.compare(password,user.password);
6.4 mongoose-sex-page
This plug-in is used to implement data paging. The collection constructor generated after importing this plug-in includes the following contents (json format):
{ "page":1,//Current page "size":2,//Number of data displayed per page "total": 8,//Total number of data "records":[ //Here are the specific data found { "_id":"5c...", "title":"ceshi" } ], "pages":4,//How many pages in total "display":[1,2,3,4]//Page number displayed by the client }
The specific use of this project can be seen in / route/article.js routing module and / views/article.art.
//article.js //1. Import mongoose sex page const pagination = require('mongoose-sex-page'); //2. Import database const {Article} = require('../../model/article'); module.exports = async (req,res) => { // Current page number passed from the client const page = req.query.page; //The objects locales can pass the added properties to the client req.app.locals.currentLink = 'article'; // Page method specifies the current page // The size method specifies the number of data bars displayed per page // The display method specifies the number of page numbers the client will display // exec method sends query request to database // Query all article data let count = await Article.countDocuments({}); let articles = await pagination(Article).find().page(page).size(2).display(3).populate('author').exec(); // res.send(articles.records); // return; res.render('admin/article.art',{ articles:articles, count:count }); }
At this time, the data articles transferred to the article.art page are in article.records.
6.5 Joi
Joi is the rule description and validator for JS objects.
In the project, I use it to verify whether the data of the database meets the requirements. The installation is also very simple, just type npm i Joi on the command line.
Then I create a verifier and export it by default, so that I can directly import the verifier to use when I need to verify the data. If the verification fails, an error will appear. We just need to determine whether we catch the error.
//model/user.js const Joi = require('Joi'); ... const validateUser = user => { //Define validation rules const schema = { username:Joi.string().min(2).max(12).required().error(new Error('User name does not conform to validation rules')), email:Joi.string().email().required().error(new Error('Email format does not meet the requirements')), // regex (regular expression), range in A-Z, A-Z, 0-9, minimum 3, maximum 30 password:Joi.string().regex(/^[a-zA-Z0-9]$/).required().error(new Error('Password format does not meet the requirements')), // The valid method tells the client to pass only normal and admin, which are illegal role:Joi.string().valid('normal','admin').required().error(new Error('Illegal role value')), state:Joi.number().valid(0,1).required().error(new Error('Illegal status value')) }; //Implementation verification return Joi.validate(user,schema); } ... module.exports = { User, validateUser }
6.6 express-session
There is such a problem when logging in. We enter the account and password in login.art, and then automatically jump to the user list page according to the code. However, are we really logged in? In fact, even if we login successfully, the next time we visit the server, the server still doesn't recognize it. Therefore, there are some knowledge points about cookie s and session s.
Cookie is a storage space opened up by browser for pages, and session is a storage space opened up by server for users who visit. When a user accesses the server, the server needs to generate a sessionId to uniquely identify the user identity and store the sessionId in the client cookie. Then, the next time you visit the server, take this sessionId to access the server, and the server responds to the information that can only be obtained after the user logs in. Here, we use express session to implement session function.
//app.js // Import express session const session = require('express-session'); //Configure session app.use(session({ secret:'secret key', saveUnitialized:false, cookie:{ maxAge:24*60*60*1000 } }));7, A record of stepping on a pit
7.1 after installing bcrypt, an error occurred in require(bcypt).
When using bcrypt, there was an error [laughing and crying] because I wrote it like this. The installation is npm i bcrypt and then the import is require('bcrypt '). But in fact, you need to install bcryptjs under the window, and then require('bcryptjs') does not understand the principle for the time being. Maybe because the node.js version is different?
7.2 when the article is modified, the picture will not be displayed after clicking the modified file without uploading.
In the article editing page, the expected function is that if I don't upload the cover image again, the image will be the image uploaded when I first create the article data by default. But in formatible, no matter whether there are pictures or not, as long as I don't upload the pictures, he will default the files to be empty, and then he will generate a 0kb picture by himself, so that if I don't upload a picture again, I will click to submit the article cover picture to be empty.
So I added a judgment in the routing module article-modify.js: if I didn't upload the picture again, the files.cover.name would be empty, then I would directly take the cover in the database and update it.
let cover = ""; if(!files.cover.name){ let article = await Article.findOne({_id:id}); cover = article.cover; } else { cover = files.cover.path.split('public')[1]; } // res.send(cover); await Article.updateOne({_id:id},{ title:fields.title, author:fields.author, publishDate:fields.publishDate, cover:cover, content:fields.content });Project address
Project address: Github->https://github.com/molifenge/selfblog
Running project:
nodemon app.js
Then, enter localhost:8080/admin/login in the browser to access it.
![](/images/blog/9a279c5dbf0efc22a797981926f76ef3.jpg)
![](/images/blog/068be7e19a8fd7ea1cc73db7ddbb42c1.jpg)