Catalog
Preface
A simple example of building a website with Node.js+Express was written earlier: http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.html
This article is built with Node.js+Koa2 as an example.
Koa: https://github.com/koajs/koa
http://koa.bootcss.com (Chinese)
Koa has not introduced much, and has written Express before. The same team has created Express articles before. By contrast, we can see some advantages.
Construction project and other preparatory work
CREATE DATABASE IF NOT EXISTS nodesample CHARACTER SET UTF8; USE nodesample; SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `userinfo`; CREATE TABLE `userinfo` ( `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key', `UserName` varchar(64) NOT NULL COMMENT 'User name', `UserPass` varchar(64) NOT NULL COMMENT 'User password', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User Information Table';
Install koa-generator: https://github.com/17koa/koa-generator
npm install -g koa-generator
The following figure after successful installation (version: 1.1.16)
Then create the Koa2 project and install the dependencies
cd working directory koa2 project name cd project directory & NPM install
Other Requirements Packages for Installation Project
1. Install packages needed to use MySQL
npm install --save mysql
If you haven't used it, you can read the related operation articles I wrote before. http://www.cnblogs.com/zhongweiv/p/nodejs_mysql.html
2. Install EJS (koa2 defaults to jade, I am used to ejs)
npm install --save ejs
If you haven't used it, you can read the related operation articles I wrote before. http://www.cnblogs.com/zhongweiv/p/nodejs_express.html
3. Install Session to store related packages (stored in redis)
npm install koa-session https://github.com/koajs/session
npm install --save koa-session
koa-session-redis https://github.com/Chilledheart/koa-session-redis
npm install --save koa-session-redis
Remove redundant files and re-plan project catalogs
1. Delete the files under views and routes that came with the creation of the project
2. Re-plan the project catalogue as follows
Explanation of Catalogue Rules:
1. Add new pub directory: mainly for the unified storage of "data access", "business logic", "public method files", "database help files", "configuration files" and so on.
2. Add utils directory under pub directory: mainly for the uniform storage of public files such as "common function file", "return value file", "enumeration file" and so on.
3. Add config directory under pub directory: Mainly for storing all kinds of configuration files uniformly
4. Add db directory under pub directory: Mainly for storing all kinds of database help classes, such as "mysql-helper.js", "mongo-helper.js" and so on.
5. Add the model directory under the pub directory: Mainly for the unified storage of various database tables CURD operation
6. Add bll directory under pub directory: mainly for the unified storage of the specific implementation of various business logic
configuration file
As can be seen from the figure above, I created a new config.js under the new config directory under pub.
In this config.js, you will write the configurations required in the "development environment" and "publishing environment" with the following code
/** * configuration file */ //Release configuration const production = { //Server Port SERVER_PORT : 3000, //REDIS To configure REDIS: { host: 'localhost', port: 6379, password: "abcd", maxAge: 3600000 }, //MYSQL Database Configuration MYSQL: { host: "localhost", user: "root", password: "abcd", port: "3306", database: "nodesample", supportBigNumbers: true, multipleStatements: true, timezone: 'utc' } } //Development and configuration const development = { //Server Port SERVER_PORT : 3000, //REDIS To configure REDIS: { host: 'localhost', port: 6379, password: "abcd", maxAge: 3600000 }, //MYSQL Database Configuration MYSQL: { host: "localhost", user: "root", password: "abcd", port: "3306", database: "nodesample", supportBigNumbers: true, multipleStatements: true, timezone: 'utc' } } const config = development module.exports = config
Plan sample routing and create new related files
The example will have the function of registration and login. First, we plan the routing, create new files under routes and views (such as files in project catalog diagram), and modify app.js files.
const Koa = require('koa') const app = new Koa() const views = require('koa-views') const json = require('koa-json') const onerror = require('koa-onerror') const bodyparser = require('koa-bodyparser') const logger = require('koa-logger') const config = require('./pub/config/config.js'); const session = require('koa-session'); const RedisStore = require('koa2-session-redis'); const index = require('./routes/index') const reg = require('./routes/reg') const login = require('./routes/login') const logout = require('./routes/logout') // error handler onerror(app) // middlewares app.use(bodyparser({ enableTypes:['json', 'form', 'text'] })) app.use(json()) app.use(logger()) app.use(require('koa-static')(__dirname + '/public')) app.use(views(__dirname + '/views', { extension: 'ejs' })) // logger app.use(async (ctx, next) => { const start = new Date() await next() const ms = new Date() - start console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) }) app.keys = ['Porschev']; const redis_conf = { key: 'Porschev', maxAge: config.REDIS.maxAge, overwrite: true, httpOnly: true, rolling: false, sign: true, store: new RedisStore({ host: config.REDIS.host, port: config.REDIS.port, password: config.REDIS.password }) }; app.use(session(redis_conf, app)); // routes app.use(index.routes(), index.allowedMethods()) app.use(reg.routes(), reg.allowedMethods()) app.use(login.routes(), login.allowedMethods()) app.use(logout.routes(), logout.allowedMethods()) // error-handling app.on('error', (err, ctx) => { console.error('server error', err, ctx) }); app.listen(config.SERVER_PORT, () => { console.log(`Starting at port ${config.SERVER_PORT}!`) }); module.exports = app
Pay attention to the modifications or additions to the red markers
Implementing Data Access and Business Logic Related Approaches
1. Write a mysql-helper.js to facilitate operation in connection pool
const config = require('./../config/config.js') const mysql = require("mysql") const pool = mysql.createPool(config.MYSQL) let query = function(sql, args) { return new Promise((resolve, reject) => { pool.getConnection(function(err, connection) { if (err) { resolve(err) } else { connection.query(sql, args, (err, result) => { if (err) { reject(err) } else { resolve(result) } connection.release() }) } }) }) } module.exports = { query }
2. Write data access related methods (userinfo.js in the model directory), as follows
const mysqlHelper = require('./../db/mysql-helper.js') const userinfo = { /** * Add a data * @param {object} args parameter * @return {object} Result */ async add ( args ) { let sql = 'INSERT INTO userinfo(UserName, UserPass) VALUES(?, ?)' let params = [args.username, args.userpass] let result = await mysqlHelper.query(sql, params) return result }, /** * Get a piece of data from UserName * @param {object} args parameter * @return {object} Result */ async getByUserName( args ){ let sql = 'SELECT Id, UserName, UserPass FROM userinfo WHERE UserName = ?' let params = [args.username] let result = await mysqlHelper.query(sql, params) return result }, /** * Get the number based on UserName * @param {object} args parameter * @return {object} Result */ async getCountByUserName( args ){ let sql = 'SELECT COUNT(1) AS UserNum FROM userinfo WHERE UserName = ?' let params = [args.username] let result = await mysqlHelper.query(sql, params) return result }, } module.exports = userinfo
3. Plan the return value (retcode.js in utils directory) before writing business logic
retcode.js/* * Return code */ const RetCode = { SessionExpired: -1, //session Be overdue Fail: 0, //fail Success: 1, //Success ArgsError: 2, //Parameter error UserExisted: 10, //Users already exist UsernameOrPasswordError: 11, //Error in username or password UserNotExist: 12, //user does not exist }; module.exports = RetCode
4. Write business logic such as "login" and "registration" (userinfo.js under bll)
const usermodel = require('./../model/userinfo.js') const retCode = require('./../utils/retcode.js') const userinfo = { /** * register * @param {object} ctx context * @return {object} Result */ async register ( ctx ) { let form = ctx.request.body const args = { username: form.username, userpass: form.userpass } let result = { code: retCode.Success, data: null } //Validation is not empty. if(!args.username || !args.userpass){ result.code = retCode.ArgsError return result } //Get the number of users based on the username let userNumResult = await usermodel.getCountByUserName(args) //User name has been registered if(userNumResult[0].UserNum > 0){ result.code = retCode.UserExisted return result } //Insert registration data let userResult = await usermodel.add(args) if(userResult.insertId <= 0){ result.code = retCode.Fail return result } return result }, /** * Sign in * @param {object} ctx context * @return {object} Result */ async login ( ctx ) { let form = ctx.request.body const args = { username: form.username, userpass: form.userpass } let result = { code: retCode.Success, data: null } //Validation is not empty. if(!args.username || !args.userpass){ result.code = retCode.ArgsError return result } //Getting User Information Based on User Name let userResult = await usermodel.getByUserName(args) //user does not exist if(userResult.length == 0){ result.code = retCode.UserNotExist return result } //ERROR Incorrect username or password if(userResult[0].UserName != args.username || userResult[0].UserPass != args.userpass){ result.code = retCode.UsernameOrPasswordError return result } //Users ID Deposit in Session in ctx.session = {id: userResult[0].Id} return result }, } module.exports = userinfo
register
1.views directory reg.ejs
reg.ejs<html> <head> <title>Nodejs Learning Notes (Fifteenth Five)--- Node.js + Koa2 A simple example of building a website</title> </head> <body> <h1><%= title %></h1> //Login name:<input type="text" id="txtUserName" maxlength="20" /> <br/> <br/> //Password:<input type="password" id="txtUserPwd" maxlength="12" /> <br/> <br/> //Password:<input type="password" id="txtUserRePwd" maxlength="12" /> <br/> <br/> <input type="button" id="btnSub" value="register" /> </body> </html> <script src="/javascripts/jquery-1.11.2.min.js" type="text/javascript"></script> <script src="/javascripts/md5.js" type="text/javascript"></script> <script type="text/javascript"> $(function(){ $('#btnSub').on('click', function(){ var $txtUserName = $('#txtUserName'), txtUserNameVal = $.trim($txtUserName.val()), $txtUserPwd = $('#txtUserPwd'), txtUserPwdVal = $.trim($txtUserPwd.val()), $txtUserRePwd = $('#txtUserRePwd'), txtUserRePwdVal = $.trim($txtUserRePwd.val()); if(txtUserNameVal.length == 0){ alert('User name cannot be empty'); return false; } if(txtUserPwdVal.length == 0){ alert('Password cannot be empty'); return false; } if(txtUserRePwdVal.length == 0){ alert('Duplicate passwords cannot be empty'); return false; } if(txtUserPwdVal != txtUserRePwdVal){ alert('Two password inconsistencies'); return false; } $.ajax({ url: '/reg', type: 'POST', dataType: 'json', data: { username: txtUserNameVal, userpass: hex_md5(txtUserPwdVal) }, beforeSend: function (xhr) {}, success: function (res) { if (res != null && res.code) { var retVal = parseInt(res.code); switch (retVal) { case 2: alert('Incorrect input'); break; case 0: alert('login has failed'); break; case 1: alert('login was successful!'); location.href = '/login' break; case 10: alert('User Registered'); break; } } else { alert('operation failed'); } }, complete: function (XMLHttpRequest, textStatus) {}, error: function (XMLHttpRequest, textStatus, errorThrown) { alert('operation failed'); } }); }) }); </script>