起步
Node 和 Mongodb 安装
网上有很多node和mongodb的安装教程,这里就不一一描述了,可以随便在网上教程,按照教程一步一步操作就好了.
构建项目
- 定位到项目目录
- 创建项目文件夹或者手动创建
mkdir admin-server
- 打开 admin-server
cd admin-server
- 安装 koa
git init -y
npm install koa -S
- 创建主程序入口 app.js
touch app.js
- 创建初始服务
// 引入koa
const Koa = require('koa')
const app = new Koa()
// 启动服务
// 监听3000端口
app.listen(3000, () => {
console.log('[Koa] Server is starting at port 3000!')
})
代码连接 mongodb
菜鸟教程有相关的 mongodb教程 可以查询 mongodb 的相关基础操作
推荐使用 mongodb 可视化工具 Robo 3T
连接 mondodb 前,需要启动mongodb服务
- 进入 mongodb 安装目录启动 mongod
// 我的本地路径
cd /usr/local/mongodb/bin
suod mongod
- 项目目录 admin-server 下创建 database
- 安装 mongoose
npm install mongoose -S
- 在 database 创建 index.js
// /admin/database/index.js
// 1. 引入mongoose库
const mongoose = require('mongoose')
// 2. 数据库地址
const DB_ADDRESS = 'mongodb://localhost:27017/admin-server'
//3. 连接数据库
mongoose.connect(DB_ADDRESS, {useNewUrlParser: true}, err => {
if (err) {
console.log('[Mongoose] database connect failed!')
} else {
console.log('[Mongoose] database connect success!')
}
})
module.exports = mongoose
- 修改入口文件 app.js 配置
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const router = require('./api')
const mongoose = require('./database')
app.listen(3000, () => {
console.log(`[Koa]Server is starting at port 3000`)
})
至此,项目的起步工作都已完成!
开发
路由(接口开发)
- 安装 koa-router 库
npm install koa-router -S
- 在项目根目录下创建 api 文件夹,并在文件下创建 modules 文件夹和路由出口模块 index.js
// app.js
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
// 引入路由
const router = require('./api')
const mongoose = require('./database')
const app = new Koa()
app.use(bodyParser())
// 挂载路由
app.use(router.routes())
.use(router.allowedMethods())
app.listen(3000, () => {
console.log(`[Koa]Server is starting at port 3000`)
})
- 编写路由出口文件 /api/index.js
// 引入组件
const Router = require('koa-router')
// 引入路由模块
const userRouter = require('./modules/user')
// 实例化
const router = new Router()
// 注册路由
router.use('/user', userRouter.routes(), userRouter.allowedMethods())
// 导出路由
module.exports = router
- 编写具体路由 /api/modules/user.js /api/modules/todo.js
const Router = require('koa-router')
// 实例化路由
const router = new Router()
// 注册get方法
router.get('/login', async (ctx, next) => {
ctx.body = {
code: 1,
msg: 'success'
}
})
// 注册post方法
router.post('/register', async (ctx, next) => {
ctx.body = {
code: 1,
msg: 'success'
}
})
module.exports = router
const Router = require('koa-router')
const mongoose = require('mongoose')
const Todo = require('../../database/schema/Todo')
const router = new Router()
router.post('/save', async (ctx, next) => {
const req = ctx.request.body
const todoItem = {
userId: req.userId,
content: req.content,
status: req.status
}
const todo = new Todo(todoItem)
const result = todo.save()
if (result) {
ctx.body = {
code: 1,
msg: 'success'
}
} else {
ctx.body = {
code: 0,
msg: 'failed'
}
}
})
router.post('/update', async (ctx, next) => {
const req = ctx.request.body
const res = await Todo.updateOne({
_id: mongoose.Types.ObjectId(req._id)
}, {
status: req.status === '0' ? 1 : 0
})
if (res.nModified === 1) {
ctx.body = {
code: 1,
msg: 'success'
}
} else {
ctx.body = {
code: 0,
msg: 'failed'
}
}
})
module.exports = router
- 修改 /api/index.js
const Router = require('koa-router')
const userRouter = require('./modules/user')
// 引入 todo
const todoRouter = require('./modules/todo')
const router = new Router()
router.use('/user', userRouter.routes(), userRouter.allowedMethods())
// 挂载 todo
router.use('/todo', todoRouter.routes(), todoRouter.allowedMethods())
module.exports = router
koa-bodyparser 中间件不支持 form-data 类型,因此post类型是 form-date 时使用ctx.request.body获取的值为空,可以使用 x-www-form-urlencoded 发送 post 参数,或者使用 koa-body 中间件代替
jwt鉴权
jwt是JSON Web Token的简称,是目前最流行的跨域身份验证解决方案,基本流程是前通过接口登录成功后,拿到后台返回 token 保存在本地,在请求其他需要鉴权接口时将 token 放入请求头 Authorization 字段中,后台判断 token 是否过期,过期则返回 401 或者其他在操作提示.
- 安装 koa-jwt 和 jsonwebtoken
npm install koa-jwt jsonwebtoken -S
- 项目根目录创建 /utils/token.js
// /utils/token.js
// 引入jsonwebtoken
const JWT = require('jsonwebtoken')
// 密钥
const JWT_SECRET = 'token'
// 登录请求是通过获取用户的 username 和 _id 生成 token 方法
exports.createToken = (data, expiresIn = '1h') => {
const { username, _id } = data
let opt = {
username,
_id
}
// 过期时间
const exp = { expiresIn }
return JWT.sign(opt, JWT_SECRET, exp)
}
// 用户请求其他需要鉴权接口是解析 header,返回 authorization
// 请求头 authorization 携带 token 时 需拼接 Bearer 格式如: `Bearer ${token}`,否则会报错
// 因此,解析token时需要对 authorization 字段做处理
exports.parseHader = ctx => {
if (!ctx || !ctx.request || !ctx.request.header || !ctx.request.header.authorization) return null
return ctx.request.header.authorization
}
// 解析 token
exports.decodeToken = token => {
return JWT.decode(token)
}
exports.JWT_SECRET = JWT_SECRET
- 修改登录接口
// /api/modules/user.js
const Router = require('koa-router')
// 引入 createToken
const { createToken } = require('../../utils/token')
router.get('/login', async (ctx, next) => {
const req = ctx.request.body
const user = await User.findOne({
username: req.username,
password: req.password
})
if (user) {
let token = createToken(user)
ctx.body = {
code: 1,
msg: '登录成功',
data: {
token
}
}
} else {
ctx.body = {
code: 0,
msg: '用户名或密码不正确'
}
}
})
完成以上步骤后,重启服务,测试登录接口就会发现,我们已经拿到需要的 token 了
- 修改 /app.js ,统一拦截 token,设置不需要拦截的路由
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
// 引入 jwt
const jwt = require('koa-jwt')
// 引入密钥
const { JWT_SECRET } = require('./utils/token')
const router = require('./api')
const mongoose = require('./database')
const app = new Koa()
app.use(bodyParser())
// jwt 拦截错误处理,被 jwt 拦截后会返回 401
app.use((ctx, next) => {
return next().catch(err => {
if (err.status === 401) {
ctx.status = 401
ctx.body = {
code: 401,
mag: '暂无权限'
}
} else {
throw err
}
})
})
// 挂载 jwt 中间件,并设置不需要拦截的路由
app.use(
jwt({ secret: JWT_SECRET})
.unless({
path: [
/^\/user\/login/,
/^\/user\/register/,
]
})
)
app.use(router.routes())
.use(router.allowedMethods())
app.listen(3000, () => {
console.log(`[Koa]Server is starting at port 3000`)
})
挂载 jwt 中间件需要放在挂载路由之前
- 验证 token
在 /utils/token.js 添加 token 验证中间件
exports.verify = () => {
return async (ctx, next) => {
let token = this.parseHader(ctx)
try {
let decode = JWT.verify(token, JWT_SECRET)
let { _id } = decode
if (_id) {
ctx.status = 200
await next()
}
} catch (err) {
// 容错 过滤掉 koa-jwt 中 unless 设置的路由
if (token == null) {
await next()
} else {
ctx.body = {
code: 401,
msg: 'token 验证错误'
}
}
}
}
}
验证通过要设置 status = 200 ,否则无法返回正确数据
在 /app.js 引入 token 验证中间件,并使用
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const jwt = require('koa-jwt')
const { JWT_SECRET, verify } = require('./utils/token')
const router = require('./api')
const mongoose = require('./database')
const app = new Koa()
app.use(bodyParser())
app.use((ctx, next) => {
return next().catch(err => {
console.log(err)
if (err.status === 401) {
ctx.status = 401
ctx.body = {
code: 401,
mag: '暂无权限'
}
} else {
throw err
}
})
})
app.use(
jwt({ secret: JWT_SECRET})
.unless({
path: [
/^\/user\/login/,
/^\/user\/register/,
]
})
)
app.use(verify())
app.use(router.routes())
.use(router.allowedMethods())
app.listen(3000, () => {
console.log(`[Koa]Server is starting at port 3000`)
})
- 使用 token 信息
修改 /api/modules/todo.js,增加获取用户 todo 列表接口
router.post('/list', async (ctx, next) => {
const token = parseHader(ctx)
const tokenDecoded = decodeToken(token)
const { _id } = tokenDecoded
const todoList = await Todo.find({
userId: _id
})
if (todoList) {
ctx.body = {
code: 1,
data: todoList,
msg: '成功'
}
} else {
ctx.body = {
code: 0,
msg: '失败'
}
}
})
// 测试接口结果
{
"code": 1,
"data": [
{
"content": "测试todo-1",
"_id": "5d43b0670f4dc43336b3ea38",
"userId": "5d43ac2f854fb42f399e142e",
"status": 0,
"createdAt": "2019-08-02T03:39:19.305Z",
"updatedAt": "2019-08-02T03:39:19.305Z",
"__v": 0
},
{
"content": "测试todo-2",
"_id": "5d43b11d6685db340dc882d1",
"userId": "5d43ac2f854fb42f399e142e",
"status": 0,
"createdAt": "2019-08-02T03:42:21.865Z",
"updatedAt": "2019-08-02T03:42:21.865Z",
"__v": 0
}
],
"msg": "成功"
}
未完,待续...