起步
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": "成功"
}
複製程式碼
未完,待續...