MVC模式構建NodeJS+Express+Mysql純後端項目

前言

最近在本身擼一個完整項目,忽然想用node來寫後端的接口。
找了找網上的資料,發現大部分都是ssr,不多有人寫相似的文章來講純node的後端項目目錄結構應該如何搭建。
所以嘗試在這裏採用傳統後端的MVC模式,結合本身的項目總結一篇從零搭建的文章,若有不足,還請大佬指教🙏。html

一、MVC模式/目錄初始化

關於MVC模式你們應該都不陌生,也就是Model-View-Controller(模型-視圖-控制器) 模式。所以在本項目中:node

  • Model:數據層
  • Controller:控制層

項目目錄的初始化結構定義以下:
mysql

由於使用 expressrouter來處理請求,所以在 view中定義的 router.js來統一管理接口。

因爲項目還須要鏈接數據庫,所以增長libsconfig目錄用於管理數據庫的鏈接和配置,再增長static文件夾用來存放靜態資源,utils存放工具函數,最終項目的目錄結構以下(因爲項目邏輯不是很複雜,暫定如此,但願獲得你們的指正):git

二、Model/View

因爲view層只用於管理接口,因此比較簡單,具體的邏輯放在Controller層處理github

// view/router.js
const express = require('express');
const router = express.Router();
const controller = require('../controller');

// 查詢
router.get('/login', controller.user.login);
複製代碼

關於Model層的初始化,能夠先定義數據庫的鏈接,爲了方便後期更改配置,將數據庫的配置單獨放在config中:sql

// config/index.js
module.exports = {
    DB_HOST: 'localhost',
    DB_USER: 'root',
    DB_PASSWORD: '*****',
    DB_PORT: '3306',
    DB_NAME: '*****'
};
複製代碼

而後在libs下初始化數據庫的鏈接數據庫

const mysql = require('mysql');
const DB_CONFIG = require('../config');
const co = require('co-mysql');

const pool = mysql.createPool({
    host: DB_CONFIG.DB_HOST,
    user: DB_CONFIG.DB_USER,
    password: DB_CONFIG.DB_PASSWORD,
    port: DB_CONFIG.DB_PORT,
    database: DB_CONFIG.DB_NAME,
});

module.exports = co(pool);
複製代碼

這裏使用了co-mysql庫配合async await來處理node-mysql的地獄回調。原理很簡單,短短的幾行代碼,感興趣的同窗能夠去GitHub上看一下。express

數據庫的鏈接初始化完成後,在model目錄下根據不一樣的需求分別編寫SQL語句,處理增刪改查等操做,以登錄接口爲例:後端

// model/login.js
const db = require('../libs/database');

module.exports = async (data) => {
    const { name, password, phone } = data;
    const searchKey = name ? 'user_name' : 'phone';
    const searchValue = name ? name : phone;
    const searchSQL = `SELECT * FROM user_info WHERE ${searchKey} = "${searchValue}" AND password = "${password}"`;
    return await db.query(searchSQL);
};
複製代碼

最終返回數據庫的處理結果,也能夠在這裏作一些處理再包一層promise返回,至此,Model層和View層都基本完成了。promise

三、Controller

Controller中,須要Model層和View層進行關聯,將經過統一的modules目錄來管理不一樣模塊的邏輯,最後暴露一個controller對象以便View層調用。

// index.js
const user = require('./modules/user');
const home = require('./modules/home');
const items = require('./modules/items');
const order = require('./modules/order');

module.exports = {
    user,
    home,
    items,
    order
};
複製代碼

這樣分層的做用是,在處理不一樣模塊邏輯時只須要調用對應模塊下對應功能的控制器就能處理請求,不一樣模塊間若是有關聯也能夠在對應模塊中進行調用。

這裏以登錄註冊接口爲例,定義在user對象下:

const resJson = require('../../utils/resJson');
const tokenUtils = require('../../utils/token');
const loginModel = require('../../model/login');
const registerModel = require('../../model/register');
const verifyModel = require('../../model/verifyRegister');
const md5 = require("md5");

const user = {
    // 登錄
    login: async (req, res) => {
        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
        const {name, password, phone} = req.body;
        await loginModel({name, password: md5(password), phone}).then(data => {
            if (data.length) {
                const tokenData = {
                    name: data[0].user_name,
                    password: data[0].password,
                    phone: data[0].phone,
                    user_uid: data[0].user_uid
                };
                res.end(resJson.returnSuccess({token: tokenUtils.getToken(tokenData)}));
            } else {
                res.end(resJson.returnError(500, '用戶名或密碼錯誤'));
            }
        }).catch(e => {
            res.end(resJson.returnError(500, '用戶名或密碼錯誤'));
        })
    },
    // 註冊
    register: async (req, res) => {
        let flag = true;
        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
        const {name, password, phone} = req.body;
        // 驗證用戶名是否存在
        await verifyModel('user_name', name).then(data => {
            if (data.length) {
                res.end(resJson.returnError(500, '用戶名已存在'));
                flag = false;
            }
        }).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if (!flag) return;
        // 驗證手機號是否存在
        await verifyModel('phone', phone).then(data => {
            if (data.length) {
                res.end(resJson.returnError(500, '手機號已存在'));
                flag = false;
            }
        }).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if (!flag) return;

        await registerModel(req.body).catch(e => {
            res.end(resJson.returnError(500, e));
            flag = false;
        });
        if (!flag) return;

        await loginModel({name, password: md5(password), phone}).then(data => {
            if (data.length) {
                const tokenData = {
                    name: data[0].user_name,
                    password: data[0].password,
                    phone: data[0].phone,
                    user_uid: data[0].user_uid
                };
                res.end(resJson.returnSuccess({token: tokenUtils.getToken(tokenData)}));
            } else {
                res.end(resJson.returnError(500, '用戶名或密碼錯誤'));
            }
        }).catch(e => {
            res.end(resJson.returnError(500, '用戶名或密碼錯誤'));
        })
    }
};

module.exports = user;
複製代碼

最後在router.js中只須要調用user中的login方法就能夠實現接口的功能。

總結

這種模式設計的項目總體結構比較清晰,基本思路以下:

  • 用戶發起請求,express-router處理請求

  • 而後node server解析該請求,定位到對應的controller裏面執行具體的操做。

  • 進入controller層,執行某項須要操做數據庫的操做。

  • 進入model層,則調用model層對應的SQL語句。

  • 而後由controller層將數據返回給用戶。

以上就是從零搭建該項目的整個過程,若有不足,還請多多指正!

相關文章
相關標籤/搜索