引言
由于前端部分设计到数据交互,而前端做的只是发送请求获取数据之后进行处理,大部分的数据逻辑写在后端,这段时间就学习了一部分内容用node+mysql实现后端的简单业务:
准备内容:
vue2
nodejs
mysql数据库
navicat
正文:
一、首先准备mysql连接本地的数据库,网上都有教程,建议使用navicat去连接,使用命令行的方式个人认为比较麻烦~
二、创建一个文件夹module,创建一个db.js文件
const {Sequelize, Model} = require('sequelize')
const loger = require("../log.config")
//创建数据库
const db = new Sequelize('sso','root','zjh123456',{
host: 'localhost',
dialect: "mysql",
ssl: true,
protocol: "postgres",
logging: (msg)=>{
loger.sqlLoger.info(msg)
},
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false
}
}
})
/**
* 数据库的验证方法
* @method
* @authenticate
*/
db.authenticate().then(() => {
console.log("链接数据库成功-----");
}).catch(err => {
console.error("不能连接到数据------",err);
})
//输出数据库
module.exports = db
这里我使用sequelize来连接数据库,sequelize是基于NodeJs的ORM框架,它适用于不同的数据库,可以通过sequelize对数据库进行一系列的操作,new Sequelize来创建一个连接池,他的参数有:
@param {String} database 要连接的数据库name
@param {String} username 登陆数据库时的用户名
@param {String} password 登陆数据库时的密码
@param {Object} options 选项配置参数,为一个对象,内含许多配置属性
比如:
{
host: 数据库主机,
port:数据库端口号
dialect: 指定要连接哪种类型的数据库,如:mysql,postgres等
define: 为模型定义默认选项
timezone: 时区
}
里面的loger是我自己设置的日志,这里可以先忽略;这里写完之后可以运行来看看是否成功,成功以后可以进行下一步的操作;
三、创建好数据库并连接之后,下来就是创建表,和设置表关系,并配置表里的数据模型,创建需要的表文件比如:book.js或者登陆要实用的user.js或者公司人员person.js
/**
* 数据库通过ORM框架链接并创建表
*/
const db = require('./db')
const Sequelize = require('sequelize')
const User = db.define('user',{
id:{
type:Sequelize.INTEGER, //对应的是列的数据类型
primaryKey:true //表示该属性对应的是表的主键列
},
username:{
type:Sequelize.STRING,
},
pass:{
type:Sequelize.STRING,
},
token:{
type:Sequelize.STRING,
},
},{
tableName:"zjh_sso_demo_user", //生成表名
freezeTableName: true,//映射表名的设置:true表示使用用户给定的表名,false表示MySQL自动生成表名(为类名后加s)
timestamps: false,//是否自动生成时间戳列(createAt列、updateAt列),false表示不生成
paranoid:true, //数据不会真正删除
})
module.exports = User
以上的文件看个人的需求来设置;引入的db文件,就为数据库连接以后sequlize提供的对象,可以使用define定义数据库表的默认项:他的参数有:
* @param {String} type 字段数据类型
* @param {Boolean} allowNull 是否允许为空
* @param {Boolean} autoIncrement 自增
* @param {Boolean,String} unique 唯一性
* @param {Boolean} primaryKey 对主键的设置
* @param {String} defaultValue 默认值的设置
* @param {String} field 表头
* @param autoIncrement 解释说明
举个例子:
* userId :{
field: 'user_id',
primaryKey: true,
type: Sequelize.BIGINT,
allowNull: false
* }
而导出的这个对象,就可以进行简单的增删改查如:
* @create 增
* 例:
* User.create ({
userId: 23,
userName: '老杨',
updateTime: '2016-01-22 18:37:22'
* })
* @destroy 删
* 例:
* User.destroy ({
where: {id:5}
* })
* @update 改
* 例:
* User.update (
{
room_status: '0'
}, {
'where': { 'id': 1 }
}
).then(ra => {
res.send({
code: 200,
data: r,
mess: ra
* })
* @findOne 查
* 例:
* User.findOne ({
where: {id:5}
* })
这里只是列举一部分,还有sequelize提供的更多的一个接口函数,这里就不一一列举,具体可以上nodejs官网上搜这个sequelize模块;
接下来就是设置表关系,我这里统一创建一个文件进行设置表关系,然后再统一的进行模型匹配,这里创建两个文件分别是relationship.js和aync.js:
/**
* 表关系
*/
const compony = require('./compony')
const person = require('./person')
compony.hasMany(person);
person.belongsTo(compony);
/**
* 同步数据库的表
*/
require("./user");
require("./compony");
require("./person");
require('./relationship')
require('./Book')
const sequelize = require('./db')
sequelize.sync({alter: true}).then(() => {
console.log("所有模型同步完成");
});
这里解释一下,现在使用的这个sequelize操作都会转化为sql内部的这个语言,我这里使用的表关系,相当于操作navicat里面的设置主键;这里我也尝试了用导入mysql2驱动的模式,使用js进行连接数据库也是可以的:
/**
* 验证连接数据库,使用sql语句
*/
const mysql = require("mysql2/promise");
const pool = mysql.createPool({
host: "localhost",
user: "root",
password: "zjh123456",
database: "sso-demo",
multipleStatements: true,
});
async function test(id) {
// 创建一个数据库连接
const sql = `select * from employee where \`name\` like concat('%', ?, '%') ;`;
const [results] = await pool.execute(sql, [id]);
console.log(results);
}
test("4");
只不过,使用工具库肯定是方便的,而且实用这个还需要写sql语言,又得花费一部分精力去学习,我们直接可以使用工具肯定是无可厚非的。
这里我们运行一下,然后刷新数据库,就可以看到库里面对应的表,以及他的键,但是还暂时没有数据,像user.js这种就可以使用上面的方法进行添加删除修改查找,但是有一个问题就是像那种很多的数据,这里不可能去手动添加,太浪费时间,这里我使用mockjs去给数据量大的表里添加数据如下
我单独创建一个mock文件夹对compony和person这两个表进行添加数据,需要数据的时候我可以直接运行这个js文件就可以了
/**
* 快速生成表数据
*/
const mock = require('mockjs')
const result = mock.mock({
"datas|10-50":[
{
"id|1-20000":0,
name:"@cname",
mobile:/1\d{10}/,
location: "@city(true)",
"componyId|1-16":0,
}
]
}).datas;
// console.log(result);
require('../module/relationship')
const person = require('../module/person')
person.bulkCreate(result)
之前导出的一个person的sequelize().define定义出来的对象,有一个bulkCreate方法可以批量导入数据。
四、逻辑层和API层
数据层我们建立好了之后就需要,有逻辑层和对外的API层(也就是相当于java上说的三层架构的web层,对应的是给前端提供的APi接口)
const compony = require("../module/compony");
exports.addcompony = async function (obj) {
const ins = await compony.create(obj);
return ins.toJSON();
};
exports.deletecompony = async function (id) {
return await compony.destroy({
where: {
id,
},
});
};
exports.updatecompony = async function (id, obj) {
return await compony.update(obj, {
where: {
id,
},
});
};
exports.getcomponyById = async function (id) {
const result = await compony.findByPk(id);
if (result) {
return result.toJSON();
}
return null;
};
exports.getcomponyes = async function () {
const result = await compony.findAll();
return JSON.parse(JSON.stringify(result));
};
这里我只示范一个compony数据库的增删改查的简单js文件,创建这个文件之后,我们可以去创建一个js文件,导入上面第二个题目中的创建,连接数据库的文件,然后再引入这个compony.js,然后调用函数,node compony.js运行试一试是否可以成功,成功之后进行这里我是用的是express框架编写本地的服务,我
/**
* 后端服务
*/
const express = require("express")
const app = express()
const path = require("path")
const cors = require("cors");
// const whiteList = ["null", "http://localhost:5008"];
app.use(
cors({
origin(origin, callback) {
if (!origin) {
callback(null, "*");
return;
}
// if (whiteList.includes(origin)) {
// callback(null, origin);
// } else {
// callback(new Error("not allowed"));
// }
callback(null,origin)
},
credentials: true,
})
);
const staticRoot = path.resolve(__dirname,"../public")
app.use("/static",express.static(staticRoot))
const cookieParser = require("cookie-parser")
app.use(cookieParser())
// 应用cookies中间件
app.use(require("../route/cookieMiddleware"));
// 应用session中间件
// app.use(require("../route/sessionMiddleware"));
// 应用jwt中间件
// app.use(require("../route/jwtMiddleware"));
app.use(express.urlencoded({
extended:true
}))
app.use(express.json())
app.use(require("../route/apiMiddleware"))
//处理api的请求
console.log("引入中间件----");
// app.use("/api/user",require("../route/api/userJwt"))
app.use("/api/user",require("../route/api/userToken"))
// app.use("/api/user",require("../route/api/userSession"))
app.use("/api/person",require("../route/api/person"))
app.use("/api/compony",require("../route/api/compony"))
app.use("/api/book",require("../route/api/book"))
app.use("/api/upload",require("../route/api/upload"))
console.log("引入中间件----");
//处理错误的中间件
app.use(require("../route/errorMiddleware"))
const port = 5008
app.listen(port,()=>{
console.log(`server listen on ${port}`);
})
这里用网上的描述来解释吧,Express 是一个第三方模块,用于快速搭建服务器(替代http模块) 基于 Node.js 平台,快速、开放、极简的 web 开发框架。保留了http模块的基本API,使用express的时候,也能使用http的APIexpress还额外封装了一些新方法,能让我们更方便的搭建服务器。
上述的代码中我使用了很多中间件,其中有处理错误的,处理请求头的,处理api接口的,处理token,session,jwt的,解析请求头,请求提的,还有访问静态资源的,还有一个这个cors跨域的,下面我直接把代码放上面,可以看看,代码中我有比较详细的注释。
/**
* 用于解析token
*/
const { getErr } = require("./getResult");
const cryptor = require("../util/crypt")
const { pathToRegexp } = require("path-to-regexp");
//需要认证的api
const needTokenApi = [
{ method: "POST", path: "/api/person" },
{ method: "PUT", path: "/api/person/:id" },
{ method: "GET", path: "/api/user/auth" },
];
// 用于解析token
module.exports = (req, res, next) => {
let token = req.cookies.token;
// 获取cokkie
// let token = req.signedCookies.token;
if(!token){
//其他的端获取token
token = req.headers.authorization
}
if(!token){
handleNonToken(req,res,next)
return;
}
const userId = cryptor.decrypt(token)
console.log(userId,"认证通过啦~~~");
next()
};
//处理没有认证的情况
function handleNonToken(req, res, next) {
res
.status(403)
.send(getErr("不好意思,您没有token令牌无法登陆哦~", 403));
}
//处理错误的中间件
const getMsg = require("./getResult")
module.exports = (err,req,res,next) => {
if(err){
const errObj = err instanceof Error? err.message:err
res.status(500).send(getMsg.getErr(errObj))
}else{
next()
}
}
/**
* 这里是提供一些函数,供其他的中间件实用
* @param {*} err
* @param {*} errCode
* @returns
*/
exports.getErr = function(err= "server error",errCode = 500){
return {
code: errCode,
msg:err
}
}
/**
*
* @param {*} result
* @returns
*/
exports.getResult = function(result){
return {
code:0,
msg:"请求成功啦,恭喜你",
data:result
}
}
exports.asyncHandler = (handler) =>{
return async (req,res,next) => {
try {
const result = await handler(req,res,next)
console.log(result);
res.send(exports.getResult(result))
} catch (error) {
console.log("调用api错误啦~~~~");
next(error)
}
}
}
/**
* jwt认证
*/
const { getErr } = require("./getResult");
// const { pathToRegexp } = require("path-to-regexp");
const jwt = require("./jwt");
// const needTokenApi = [
// { method: "POST", path: "/api/person" },
// { method: "PUT", path: "/api/person/:id" },
// { method: "GET", path: "/api/user/auth" },
// ];
// 用于解析token
module.exports = (req, res, next) => {
// const apis = needTokenApi.filter((api) => {
// const reg = pathToRegexp(api.path);
// return api.method === req.method && reg.test(req.path);
// });
// if (apis.length === 0) {
// next();
// return;
// }
const result = jwt.verify(req);
if (result) {
//认证通过
req.userId = result.id;
next();
} else {
//认证失败
handleNonToken(req, res, next);
}
};
//处理没有认证的情况
function handleNonToken(req, res, next) {
res
.status(403)
.send(getErr("不好意思,您没有token令牌无法登陆哦~", 403));
}
这里我使用express提供的router方法来定义对外的API接口服务。
const express = require("express")
const router = express.Router()
const person = require("../../serveAPI/personService")
const {asyncHandler} = require("../getResult")
router.get("/", asyncHandler(
async (req,res,next) =>{
const page = req.query.page || 1;
const limit = req.query.limit || 10;
const id = req.query.id || null;
return await person.getpersons(page,limit,id)
}
))
router.get("/:id", asyncHandler(
async (req,res,next) =>{
return await person.getpersonById(req.params.id)
}
))
router.post("/", asyncHandler(
async (req,res,next) =>{
return await person.addperson(req.body)
}
))
router.delete("/:id",asyncHandler(
async (req,res,next) =>{
return await person.deleteperson(req.params.id)
}
))
router.put("/:id",asyncHandler(
async (req,res,next) =>{
return await person.updateperson(req.params.id,req.body)
}
))
module.exports = router
当这些写完之后,就可以运行服务了,要测试的话,可以使用postman,也可以直接使用服务地址+ router拼接的方式去测试,我也是这么做的,这里推荐大家是用npx nodemoon来启动服务,这样的话,修改服务的内容,就不用重新启动服务,很方便。
五、vue-cli搭建vue2项目
上述写的服务,由于是在vue-cli搭建的工程内写的,所以我不用安装各类插件,我直接写在vue的package.json中,下载依赖的时候就可以使用了,当然有些小伙伴不习惯这种commonjs的规范不喜欢这种module.export或者require的方式,也可以使用es6中的export,import的方法,不过在启动的时候,就需要配置babelnode去解析一下运行,服务可以自己去单独启动,也可以配置在package里,在vscode中断启动如:
"starta": "cross-env NODE_ENV=ssoa babel-node server/index.js",这样启动有一个好处就是,可以不用手动在js文件中加这种node_env参数,在启动配置好以后,就可以直接在js文件中使用,这是比较方便的
这里的依赖,可以根据需要去下载配置,我这里有很多测试的东西,所以比较杂。
这里的配置,我就不一一的看了,大致就是vuex,还有api的service,再有就是路由router,还有主页面,大家可以使用axios调用接口,处理数据,写页面还是非常有意思的东西,我这里写的是一个登陆的页面,其中登陆模块下一个再去记录,就到这里吧。。。