本文是对我的个人网站后台的开发过程记录,项目通过express
搭建,数据库是mysql
。
一、项目创建
这次项目通过express-generator
创建:
# install
npm install -g express-generator
# create project
express projectname
# enter
cd projectname
# download dependencies
npm i
以上步骤完成之后,项目目录如下:
├─bin # start: node ./bin/www
├─node_modules # dependencies
├─public # static resources
│ ├─images
│ ├─javascripts
│ └─stylesheets
├─routes # routes
├─views # pages
├─app.js # app
├─package.json # config
└─package-lock.json # dependencies collective info
文件分析
./bin/www.js
创建服务器,监听端口,监听服务器error
和listening
事件。
这个文件之后没有什么需要修改的地方。
./app.js
创建app,引入路由文件、引入中间件等。
eg.
// create app
var app = express();
// route
var indexRouter = require('./routes/index');
app.use('/', indexRouter);
// middleware
app.use(cookieParser());
后期新建的路由文件、新引入的插件等都要在这里引入。
./routes/index.js
var express = require('express');
var router = express.Router();
/* for request '/' */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
二、数据库操作
在根目录下创建module
文件夹,并在其中新建db.js
模块作为数据库操作的基类:
/* import mysql */
const mysql = require('mysql');
/* create and export DB */
module.exports = class DB {
// mysql connection object
static conn = null;
// mysql connecting function
static connection () {
// create mysql connection object
DB.conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myblog',
insecureAuth: true
});
// connect
DB.conn.connect(err => {
if (err) {
console.log('Connection failed: ' + err);
}
})
}
// stop connection
static end () {
if (DB.conn != null) {
DB.conn.end();
}
}
/*
* sql query function
* ... ...
*/
static query (sql) {
return new Promise( (resolve, reject) => {
// connect mysql
this.connection();
// query
DB.conn.query(sql, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
// stop connection
this.end();
})
}
}
连接mysql数据库失败
(一)在测试数据库查询方法时 ,出现以下报错:
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
解决方法:在你的mysql安装路径下找到bin
文件夹并打开,运行cmd,如
C:\mysql\mysql\bin
# 1. login with password
mysql -u root -p
# 2. enter your password
# 3. alter mysql_native_password
alter user 'root'@'localhost' identified with mysql_native_password by 'yourRootPassword';
(二)之后又碰巧忘记root密码,此处推荐一篇博文,可以解决这个问题:
Win10下Mysql8忘记密码,登录及修改Mysql密码
提示:如果操作过程中,输入命令后既不执行也不报错,可以关闭控制台后重新打开一个窗口再输入。
三、登录功能
在./module
文件夹中新建user.js
模块,用来处理和用户相关的操作,如登录、注册等。
因为以上操作涉及对数据库的增删改查,所以需要User
继承之前创建的`DB``类。
基础版
/* import DB, create and export User */
module.exports = class User extends require('./db') {
static login (username, password) {
return new Promise((resolve, reject) => {
const sql = `select username, id from users where username=${username}&&password=${password}`;
this.query(sql).then(res => {
resolve(res);
}).catch(err => {
console.log(err);
})
})
}
}
以上代码只根据用户传入的用户名、密码做了查询,还需要在服务端生成token
,返回到客户端。
使用jsonwebtoekn
生成token
:
首先,安装jsonwebtoekn
npm install jsonwebtoken
引入
const jwt = require('jsonwebtoken');
生成
const data = { id: 'u_1234' };
const privateKey = 'hahaha';
const expireTime = { expiresIn: 60 * 60 }; // expire in 1 hour, not mandatory param
// Synchronous Sign with default (HMAC SHA256)
/*
* @params data
* @params privateKey
*/
const token = jwt.sign(data, privateKey, expireTime )
以上通过jwt的默认加密算法HMAC SHA256加密,也可以通过传入参数的方式改变加密算法。作者在使用其他加密算法时会报错Error: error:0909006C:PEM routines:get_name:no start line,可能因为这种算法需要设置public key的文件,具体操作方法不清楚。
完整版
const jwt = require('jsonwebtoken');
module.exports = class User extends require('./db') {
static login (username, password) {
return new Promise((resolve, reject) => {
const sql = `select username, id from users where username=${username}&&password=${password}`;
this.query(sql).then(res => {
if (res[0].username === username) {
const token = this.genetateToken(res[0]);
resolve({
code: 200,
msg: '登录成功’,
token
})
}
}).catch(err => {
console.log(err);
})
})
}
// generate token
static generateToken(data) {
const privateKey = 'myblog';
const token = jwt.sign(data, privateKey, { expiresIn: 60 * 60 });
return token;
}
}
在路由处理程序中使用
./routes/users.js
const express = require('express');
const router = express.Router();
const User = require('../module/user')
/* GET users listing. */
router.post('/login', function(req, res, next) {
console.log(req.body)
const username = req.body.username;
const password = req.body.password;
User.login(username, password).then(result => {
console.log(result)
res.end(JSON.stringify(result))
}).catch(err => {
console.log(err)
})
});
module.exports = router;
app.js
中引入users.js
时定义的路由是/users
:var usersRouter = require('./routes/users'); app.use('/users', usersRouter);
所以完整的登录接口是
/users/login
四、跨域
使用express-generator搭建的项目本身是有设置跨域响应头的,但不知道为什么我在前端项目中发送请求时依然存在跨域问题。为了解决这个问题,我使用了第三方插件cors
首先,安装
npm i cors
引入
const cors = require('cors')
使用
// basic
app.use(cors());
// allow specific origin
app.use(cors({
origin: 'http://localhost:8080',
optionsSuccessStatus: 200
}))
五、上传文件功能
上传文件功能通过插件formidable
实现。
/routes/upload.js
const express = require('express');
const router = express.Router();
const path = require('path');
// 引入formidable
const formidable = require('formidable');
/* upload one pic */
router.post('/', function(req, res, next) {
// 创建实例
const form = new formidable.IncomingForm();
// 解码规则
form.encoding='utf-8';
// 保留文件扩展名
form.keepExtensions=true;
// 上传路径
form.uploadDir =path.join(__dirname,"../public/images");
// 保存
form.parse(req,function(err,fields,files){
if (!err) {
res.end(JSON.stringify({fields,files}))
}
})
});
/app.js
const upload= require('./routes/upload');
app.use('/upload', upload);
前端通过
<input type="file" />
上传文件。