为啥选择express呢,是因为蛤,koa比较迷你,微内核,拓展性强,所以一些web框架例如阿里的eggjs就是基于koa。而express集成了路由和static中间件所以显得重一些(本猴只用过express)。
好了,介绍下之后会用到的技术栈:
- express
- mysql
- sequelize
- jsonwebtoken 和 express-jwt(jwt)
- node-schedule(定时任务)
项目目录:
├── bin/ // 服务配置文件
├── db/ // mysql 配置以及表的配置
├── controllers/ // 模块存放地
├── public/ // 静态资源(存放用于上传静态)
├── routes/ // 路由配置文件
├── servers/ // 数据库数据处理文件
├── tool/ // 工具函数
├── app.js // 服务器配置文件
└── package.json
接下来涉及的环境有mysql,没有的小伙伴,之前写过一篇环境配置的篇章,mysql环境配置以及搭配 sequelize 使用,我们就拿这个篇章的初始代码来敲了。
安装所需依赖包
npm i sequelize boom express-jwt express-validator jsonwebtoken multer -S
在db目录下创建两个表的描述js文件:
// user
const Sequelize = require('sequelize')
const db = require('./index') // 引入刚刚写的sequelize配置
module.exports = db.defineModel('user_list', {
u_id: { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, unique: true, autoIncrement: true },
u_name: { type: Sequelize.STRING },
u_email: { type: Sequelize.STRING },
u_capacity: { type: Sequelize.INTEGER(), defaultValue: 10 }, // 容量
u_password: { type: Sequelize.STRING, defaultValue: 123456 },
u_pic: { type: Sequelize.STRING(), defaultValue: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png' } // 用户头像
})
// file
const Sequelize = require('sequelize')
const db = require('./index')
module.exports = db.defineModel('file_list', {
f_id: { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, unique: true, autoIncrement: true },
f_name: { type: Sequelize.STRING() },
f_size: { type: Sequelize.STRING() },
f_dow_url: { type: Sequelize.STRING() },
f_type: { type: Sequelize.STRING() },
f_grouping: { type: Sequelize.STRING() },
f_transfer_state: { type: Sequelize.INTEGER, defaultValue: 0 }, // 0 未下载, 1 已下载
f_history_state: { type: Sequelize.INTEGER, defaultValue: 0 }, // 0 未删除, 1 已删除
u_id: { type: Sequelize.INTEGER },
u_name: { type: Sequelize.STRING }
})
// 创建表
// user
const user_list = require('../user_list')
user_list.sync({
force: true // 在尝试创建表之前添加了一个DROP TABLE IF EXISTS – 如果强制,现有表将被覆盖。
})
// file
const fileList = require('../fileList')
fileList.sync({
force: true
})
常用的常量
const { env } = require('./env')
const UPLOAD_PATH = env === 'dev' ? 'public/upload/users' : 'public/upload/users'
module.exports = {
CODE_ERROR: -1,
CODE_SUCCESS: 200,
PWD_SALT: 'admin_imooc_node',
PRIVATE_KEY: 'admin_imooc_node_shan',
JWT_EXPIRED: 60 * 60, // TOKEN 失效时间
CODE_TOKEN_ERROR: -2,
UPLOAD_PATH,
TOURIST_PATH: `${UPLOAD_PATH}/tourist`
// UPLOAD_URL
}
写一个返回结果控制器
const {
CODE_SUCCESS,
CODE_ERROR,
CODE_TOKEN_ERROR
} = require('../tool/constant')
class Result {
constructor(data, msg = '操作成功!', options) {
this.data = null
if (arguments.length === 0) {
this.msg = '操作成功!'
} else if (arguments.length === 1) {
this.msg = data
} else {
this.data = data
this.msg = msg
if (options) this.options = options
}
}
content() {
if (!this.code) {
this.code = CODE_SUCCESS
}
const base = {
code: this.code,
msg: this.msg
}
if (this.data) {
base.data = this.data
}
if (this.options) {
base.options = this.options
}
return base
}
success(res) {
this.code = CODE_SUCCESS
this.send(res)
}
fail(res) {
this.code = CODE_ERROR
this.send(res)
}
send(res) {
res.send(this.content())
}
tokenError(res) {
this.code = CODE_TOKEN_ERROR
this.send(res)
}
}
module.exports = Result
数据库操作封装
/*
* 根据条件查找新数据
* @params option 配置对象
* */
function findOne(example, option, cb) {
example.findOne({
where: option
}).then(res => {
cb && cb(res)
}).catch(err => {
cb && cb(err)
})
}
function findAll(example, option, cb) {
example.findAll(
option
).then(res => {
cb && cb(res)
})
}
/*
* 创建一条新数据
* @params option 配置对象
* */
function create(example, option, cb) {
example.create(
option
).then(list => {
cb && cb(list)
}).catch(() => {
cb && cb(false)
})
}
/*
* 根据ID查一条数据
* @params option 配置对象
* */
function findById(example, id, cb) {
example.findById(id).then(list => {
cb && cb(list)
}).catch(err => {
cb && cb(err)
})
}
/*
* 根据条件更新数据
* @params option 配置对象
* 示例:
* option = { firstName: "King" },
{
where: { firstName: null }
}
* */
function update(example, option, cb) {
example.update(...option).then(list => {
cb && cb(list)
}).catch(err => {
cb && cb(err)
})
}
/*
* 根据条件删除一条数据
* @params option 配置对象
* */
function destroy(example, option, cb) {
example.destroy(option).then(list => {
cb && cb(list)
}).catch(err => {
cb && cb(err)
})
}
module.exports = {
findOne,
create,
findById,
update,
destroy,
findAll
}
接下来,我们先实现一个登录接口试试看,数据库创建一条数据:
// 在工具函数上创建一个 专门处理接收参数的处理中间件(利用boom噢)
function errorChecking(next, req, cb) {
const err = validationResult(req)
if (!err.isEmpty()) {
const [{ msg }] = err.errors
next(boom.badRequest(msg))
} else {
cb && cb()
}
}
// 查询数据库
function login(options, cb) {
findOne(UserList, options, data => {
cb && cb(data)
})
}
router.post('/login', [
body('password').isLength({ min: 5 }).withMessage('密码长度太低'),
body('username').isLength({ min: 4 }).withMessage('用户名长度太低')
], (req, res, next) => {
errorChecking(next, req, () => { // 封装的错误处理中间件
let { username, password } = req.body
// 开发模式密码123456
password = password === '123456' ? password : md5(`${password}${PWD_SALT}`)
login({ u_name: username, u_password: password }, user => { // 登录查询数据库处理中间件
if (!user || user.length === 0) { return new Result('登录失败').fail(res) }
const token = jwt.sign( // 登录token
{ username },
PRIVATE_KEY,
{ expiresIn: JWT_EXPIRED }
)
new Result({ token }, '登录成功').success(res)
})
})
})
app.js 配置路由以及利用cors解决跨域
const createError = require('http-errors')
const express = require('express')
const path = require('path')
const cookieParser = require('cookie-parser')
const logger = require('morgan')
const cors = require('cors')
const indexRouter = require('./routes/index')
const { scheduleCronstyle } = require('./tool/schedule')
const app = express()
// 开启全局定时任务
scheduleCronstyle()
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use(cors())
app.use('/public', express.static(path.join(__dirname, 'public')))
app.use('/', indexRouter)
app.use(function(req, res, next) {
next(createError(404))
})
app.use(function(err, req, res, next) {
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}
res.status(err.status || 500)
res.render('error')
})
module.exports = app
我们接下来拿项目测试一下我们这个接口
确实是登录成功了哦
可能讲的有点块,因为项目东西有点多,很多基础性的东西我没讲到,只是讲一些必要性的东西,我已经整理好代码啦,大家可以去GitHub clone下来看看具体源码然后再看看文章,或许有更好的理解哦,桌面百度云源码
前端篇章: