­

Node教程——API介面開發(MangoDB+Express)

一、大綱

  • 大綱:
    關於架構,
    1. 首先我們的有一個app.js這個就是根路由起點,用來最初的打入口
      它的功能有:
      1.1 引入模組創建基礎的網站伺服器,
      1.2 導入bodyPasser,過濾還有處理我們的post請求
      1.3 導入資料庫連接
      1.4 把路由開放出去

    2. 再來一個main.js它在我的route文件夾下,
      2.1 什麼需啊喲再這裡做二次攔截,再進行分配路由,
      2.2 引入兩個邏輯處理模組,當請求發來的時候,如果不是login那麼我們就需要驗證token,
      2.3 如果訪問的是login那麼我們需要,發post數據,來處處理驗證,然後拿token,
      2.4 如果有token,那麼再來訪問2.2這裡的不是logi的其它路由路徑,的校驗就通過了,於是乎我們就能分配各種介面了

    3. 詳細的講解輔助,工具程式碼
      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
參數名稱 是否必選 參數說明
email 郵箱
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多行子,反覆的修改,才成型,希望大佬高抬貴手指點迷津,也希望,這篇入門的文檔,能給你帶來收穫,接下里,我就用這裡的模型,開發更多的介面。敬請期待。

  • 各個模組的引用關係,用例圖