最近在本身擼一個完整項目,忽然想用node
來寫後端的接口。
找了找網上的資料,發現大部分都是ssr
,不多有人寫相似的文章來講純node
的後端項目目錄結構應該如何搭建。
所以嘗試在這裏採用傳統後端的MVC
模式,結合本身的項目總結一篇從零搭建的文章,若有不足,還請大佬指教🙏。html
關於MVC
模式你們應該都不陌生,也就是Model-View-Controller
(模型-視圖-控制器) 模式。所以在本項目中:node
Model
:數據層Controller
:控制層項目目錄的初始化結構定義以下:
mysql
express
的
router
來處理請求,所以在
view
中定義的
router.js
來統一管理接口。
因爲項目還須要鏈接數據庫,所以增長libs
和config
目錄用於管理數據庫的鏈接和配置,再增長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
層將數據返回給用戶。
以上就是從零搭建該項目的整個過程,若有不足,還請多多指正!