Node教程——API介面開發(MangoDB+Express)
- 2020 年 4 月 15 日
- 筆記
- javascript, Node.js教程
一、大綱
- 大綱:
關於架構,-
首先我們的有一個app.js這個就是根路由起點,用來最初的打入口
它的功能有:
1.1 引入模組創建基礎的網站伺服器,
1.2 導入bodyPasser,過濾還有處理我們的post請求
1.3 導入資料庫連接
1.4 把路由開放出去 -
再來一個main.js它在我的route文件夾下,
2.1 什麼需啊喲再這裡做二次攔截,再進行分配路由,
2.2 引入兩個邏輯處理模組,當請求發來的時候,如果不是login那麼我們就需要驗證token,
2.3 如果訪問的是login那麼我們需要,發post數據,來處處理驗證,然後拿token,
2.4 如果有token,那麼再來訪問2.2這裡的不是logi的其它路由路徑,的校驗就通過了,於是乎我們就能分配各種介面了 -
詳細的講解輔助,工具程式碼
3.1 這裡有我們在主線邏輯里需要的一些工具
3.2 中間件Middleware,裡面有兩個工具,一個是生成token的一個校驗token的
3.3 在3.2中的功能 需要依賴model下的一個util下的jwt工具生成token生成的依據是兩個密鑰對
3.4 我們還有兩個工具,content 創建資料庫的鏈接,create初始化數據還有開發數據操作對象
-
二、展示 介面 API文檔
1.1 登錄
請求地址 | 請求方式 |
---|---|
/main/login | POST |
參數名稱 | 是否必選 | 參數說明 |
---|---|---|
是 | 郵箱 | |
password | 是 | 密碼 |
{
"status": 200,
"msg": "登陸成功",
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiNWU5MTIwMTViOWI0NmYzZmE4Y2MzMjUzIiwiZXhwIjoxNTg2OTUyMzk1LCJpYXQiOjE1ODY5NTA1OTV9.IsprCaQ_gZRh0BzS8SnAd0iJ27BOpOEb-ZGn0bTlwHVPTiYPK50wiEOL_0aAYINfNT_Mfvb726l3CpiHG9lsJ45m4eqhPeJz9TbAeQj8-ST3CAkYLrD0fhgRG9YiQ5kjVpygdR8NZEWEUV7ux-moyYe7wCoVzN9mbvAkFF3IYG0"
}
1.2 拿著token進行測試
請求地址 | 請求方式 |
---|---|
/main/text | get |
參數名稱 | 是否必選 | 參數說明 |
---|---|---|
token | 是 | 需要附上你的token。在請求頭中 |
無 | 無 | 該介面不需要傳遞參數 |
{
"status": 200,
"msg": "登陸成功"
}
注意:以上就是Node寫介面的最基礎的內容。可以在這個內容上擴展更多的介面
二、 app.js模組的詳解
app.js
//引入模組,創建簡單伺服器
const express = require('express');
//引入路徑處理模組
const path = require('path');
//引入注意使用第三方模組處理post
const bodyPaser = require('body-parser')
// 引入資料庫連接 不需要返回值
require('./model/content')
//創建伺服器
const app = express();
/*//初始化資料庫,注意reque的時候 會自動執行引入的js文件
require('./model/create')
*/
//前往小心這個要放在所有的use前面,解析我們的post請求數據
app.use(bodyPaser.urlencoded({ extended: false }));
//處理靜態資源路徑
const DataPath = path.join(__dirname, 'public');
//這個我們後面是有用的,用來操作媒體數據,最重要的就是這個路徑還有這個靜態資源對象
const StaticUtils = express.static(path.join(__dirname, 'public'));
//拿到路由操作對象
const main = require('./route/mian/main');
//開放介面路徑
//攔截請求開始匹配路由
app.use('/dataPath', (req, res) => {
res.status(200).send(JSON.stringify({ 'dataPath': DataPath }))
})
app.use(StaticUtils);
app.use('/main', main)
//監聽埠
app.listen(3000)
console.log('伺服器開啟');
三、 main.js二次路由攔截模組
main.js
const express = require('express')
//業務邏輯
const guard = require('../../Middleware/loginGuard')
const loginPash = require('../../Middleware/loginPash')
//創建路由對象
const admin = express.Router()
//驗證token
admin.use(loginPash)
//登錄路由處理
admin.post('/login', guard)
//首先要驗證,然後才是放行到對應的路由介面裡面取
admin.get('/text', require('./API/home/index'))
module.exports = admin;
- 攜帶的一個簡單的測試模組(後續可以仿照這樣的模式,寫更多的API介面功能模組)
home/index.js
module.exports = async(req, res) => {
res.send({ status: 200, msg: '登陸成功', });
}
四、 輔助工具(重點!!!)
- 資料庫連接,設計還有初始化創建模組
model/content.js && model/create.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog')
.then(() => {
console.log('資料庫連接成功');
})
.catch((erro) => {
console.log(erro);
})
const mongoose = require('mongoose');
const bcrypt = require('bcrypt')
/*使用MongDB資料庫,設計數據第一步(程式碼角度分析)
1.設定規則,Schema構造器,Schema的構造函數就是規則,注意規則是一系列對象
2.創建數據
*/
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
min: 2,
max: 10
},
email: {
type: String,
//注意 我們的郵箱憑據是用戶登錄的令牌,我們需要指定他為唯一的
unique: true, //這個時候,在插入的時候如果有重複的就給你抱一個錯誤
},
password: {
type: String,
required: true,
},
role: { //需要說明一下,這個地方角色,我們硬性規定,超級管理員是admin普通用戶是normal,為什麼不用01?因為這裡string了
type: String,
required: true
},
state: {
//我們使用O1狀態來設計這個數據欄位,默認值是0。0是啟用狀態
type: Number,
default: 0
}
})
//使用規則創建集合,
//一定要注意,我們的User就是代表了我目前最user數據集合,後期的增刪改查我們都需要用到這個東西,所有我們暴露出去給路由業務使用
const User = mongoose.model('User', userSchema)
//我們再來複習一下,如何用同步的方式獲取非同步的api結果?使用async 還有awit就可以
async function createUser() {
const salt = await bcrypt.genSalt(10)
const pass = await bcrypt.hash('123456', salt)
const user = await User.create({
username: 'lli',
email: '18376621755@163.com',
password: pass,
role: 'admin',
state: 0,
})
}
// createUser()
/* // 初始化用戶,注意我們的create返回的是一個promies
User.create({
username: 'bmlaoli',
email: '861795660@qq.com',
password: '123456',
role: 'admin',
state: 0,
})
.then(() => {
console.log('用戶創建成功');
})
.catch((error) => {
console.log('用戶創建失敗');
})*/
//注意啊,es6中如果鍵值對的名字是一樣的就可以省略值。由於我們後期還會做更多的數據集合,於是乎我這裡需要暴露一個對象出去
module.exports = {
User
}
- 另一個重要的工具模組,用來創建token 根據就是文件夾下的兩個密鑰對文件
jwt.js
/*
*描述:以下是jwt工具,生成用於訪問驗證的token
*/
// 引入模組依賴
const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
// 創建 token 類
class Jwt {
constructor(data) {
this.data = data;
}
//生成token
generateToken() {
let data = this.data;
let created = Math.floor(Date.now() / 1000);
let cert = fs.readFileSync(path.join(__dirname, '../../pem/private_key.pem')); //私鑰 可以自己生成,注意這裡要使用 密鑰對,請求百度下載,兩對密鑰對
let token = jwt.sign({
data,
exp: created + 60 * 30,
}, cert, { algorithm: 'RS256' });
return token;
}
// 校驗token
verifyToken() {
let token = this.data;
let cert = fs.readFileSync(path.join(__dirname, '../../pem/public_key.pem')); //公鑰 可以自己生成
let res;
try {
let result = jwt.verify(token, cert, { algorithms: ['RS256'] }) || {};
let { exp = 0 } = result, current = Math.floor(Date.now() / 1000);
if (current <= exp) {
res = result.data || {};
}
} catch (e) {
res = 'err';
}
return res;
}
}
module.exports = Jwt;
- 接下里是兩個具體的token校驗功能模組
我把它們都放到了middleware文件夾下
loginGuard.js && loginPash.js
const JwtUtil = require('../model/util/jwt')
const { User } = require('../model/create')
const bcrypt = require('bcrypt')
const guard = async(req, res, next) => {
//注意使用第三方模組處理post
//進圖具體的業務邏輯,解構出來我們需要的東西
const { email, password, _id } = req.body; //注意啊,由於我們的中間件處理的請求於是乎我們的req身上就有這個處理的所有數據了,這個之前有說過
console.log(req.body);
if (email.trim().length == 0 || password.trim().length == 0) {
res.status(400).send(
JSON.stringify({ message: '郵箱或密碼錯誤' })
) //注意send自動把狀態碼設置成了200,所以你需要改一下
return
}
//如果用戶存在就先找到這個用戶額資訊,注意這裡的這個非同步await
let user = await User.findOne({ email });
//這裡的user就是指向當前查詢出來的數據文檔對象
if (user) {
//比對操作,第一個參數是一個明文密碼,第二個參數我們查詢出來的加密密碼 ,方法返回一個Boolean值,對比成功就是true,非同步api可以直接加await
const isValid = await bcrypt.compare(password, user.password)
if (isValid) {
//用戶校驗成功,添加tooken
// 登陸成功,添加token驗證
let _id = user._id.toString();
// 將用戶id傳入並生成token
let jwt = new JwtUtil(_id);
let token = jwt.generateToken();
// 將 token 返回給客戶端
res.send({ status: 200, msg: '登陸成功', token: token });
//校驗成功就
next()
} else {
res.status(400).send(
JSON.stringify({ message: '郵箱或密碼錯誤' })
)
}
} else {
res.status(400).send(
JSON.stringify({ message: '郵箱或密碼錯誤' })
)
}
}
module.exports = guard
loginPash.js
const JwtUtil = require('../model/util/jwt')
//驗證token
const loginPash = function(req, res, next) {
// 我這裡知識把登陸和註冊請求去掉了,其他的多有請求都需要進行token校驗
if (req.url != '/login?') {
let token = req.headers.token;
let jwt = new JwtUtil(token);
let result = jwt.verifyToken();
// 如果考驗通過就next,否則就返回登陸資訊不正確
if (result == 'err') {
console.log(result);
console.log(req.url);
res.send({ status: 403, msg: '登錄已過期,請重新登錄' });
// res.render('login.html');
} else {
next();
}
} else {
next();
}
};
module.exports = loginPash;
五、 最後我們梳理一下,這些模組之間的關係
其實這套介面下來,我比較菜雞,用了兩天的工作之餘的時間,由於我沒怎麼接觸介面的設計,對於後台的一些設計模式還有理念不是特別懂,走了很多彎路,以上的是我寫了三次筆記,長達2000多行子,反覆的修改,才成型,希望大佬高抬貴手指點迷津,也希望,這篇入門的文檔,能給你帶來收穫,接下里,我就用這裡的模型,開發更多的介面。敬請期待。
- 各個模組的引用關係,用例圖