简介
node作为新兴的技术(对比java)其有许多还需要完善的地方,虽然定位为快速开发服务端,但是Java也有springboot框架可以实现,快速开发,其次定位为处理高并发,这个作为轻量级服务端还是有一定作用的。其扩展性较高,使得许多大厂也使用.项目源码https://github.com/yp1919597583/node-
环境配置
要想使用node开发就得先配置相应的环境,首先根据自己电脑在官网Node.js下载对应的版本,这里笔者使用的是64位,下载完成后安装
第一步:
第二步:
项目结构:
数据库表创建
1.创建node_js数据库
2.创建user表如图
3.建立博客表如图
接口开发
创建数据库配置文件
//导入mysql
const mysql=require("mysql");
const db=mysql.createPool({
host:"127.0.0.1",
user:"root",
password:"yp18783819559",
database:"nodejs"
})
//向外界暴露db,供使用
module.exports=db;
配置全局路由
const express=require("express");
const app=express();
const joi=require("joi");
//静态资源托管,让用户可以访问服务器资源
app.use(express.static('page'))
app.use(express.static('layui'))
app.use(express.static('img'))
app.use(express.static('js_tool'))
//导入cors对象
const cors= require("cors");
app.use(cors());
//配置解析表单数据的中间件
app.use(express.urlencoded({extended:false}))//只能解析x-www-form-url编码的格式\
//封装cc函数
app.use((req,res,next)=>{
res.cc=function(err,status=1){
//status 默认为1失败
//err的值可能是错误对象,也可能是错误信息字符串
res.send({
status,
message: err instanceof Error ? err.message:err
})
}
next();
})
//配置解析token的中间件(一定要在路由之前配置)
const expressJwt= require("express-jwt");
const config=require("./config");
app.use(expressJwt({secret:config.jwtScrectKey}).unless({path:[/^\/api/]}))//配置全局中间件解密token并排除以/api开头的路由,不需要身份验证
//导入并使用用户路由
const userRouter= require("./router/user")
app.use('/api',userRouter)//今后访问/user时都必须在访问前加上/api
//导入并使用博客路由
const articRouter=require('./router/boke')
app.use('/myArtic',articRouter)
//导入并使用用户信息的模块
const userInfoRouter=require("./router/userinfo");
app.use('/my',userInfoRouter);//挂载到全局并加上/my前缀
//在路由之后定义错误级别的中间件(错误中间件必须写next)
app.use((err,req,res,next)=>{
if(err instanceof joi.ValidationError){
//验证失败的错误
res.cc(err);
}
//token验证失败
if(err.name==="UnauthorizedError"){
return res.cc("身份认证失败!");
}
//未知错误
res.cc(err);//貌似现在可以不用 return 嘿嘿! 笔者未报错
})
app.listen(8080,function(){
console.log("api 服务器启动 http://localhost:8080");
})
创建登录注册接口
1.创建schem验证规则
//导入定义验证规则的包
const joi= require("joi");
//定义用户名,密码,邮箱的验证规则(注意:此处定义的变量名必须与参数上的一致)
const userName=joi.string().min(1).max(12).required()//.alphanum()限制必须是字母与数字
const password=joi.string().alphanum().pattern(/^[\S]{6,12}$/).required()//定义正则表达式
const email=joi.string().email();
//定义,id nickname,email 验证规则
const id=joi.number().integer().min(1).required();
const nickName=joi.string().required();
//定义验证头像的验证规则
const avatar=joi.string().dataUri().required();//dataUri() 表示必须是base64的字符串
//向外暴露一个用来验证的规则
//定义验证注册和登录表单验证的规则
exports.reg_login_schems={
body:{
userName,
password,
email
},
}
//更新用户信息验证
exports.update_userinfo_schema={
//需要对req.body的数据验证
body:{
id:id,
nickName:nickName,
email:email
}
}
//更新密码验证
exports.update_password_schema={
body:{
oldPwd:password,
newPwd:joi.not(joi.ref('oldPwd')).concat(password)
/*
1.joi.ref('oldPwd')表示 新密码与旧密码一致
2.joi.not(joi.ref('oldPwd'))表示 新密码与旧密码不一致
3.concat(password) 表示合并password验证规则
*/
}
}
//向外共享,验证头像的规则
exports.update_avatar_schema={
//验证body中数据的合法性
body:{
avatar,//参数名,与验证规则名重名可省略
}
}
2.创建全局token加密解密秘钥(对称加密)
module.exports={
//加密,解密token的秘钥
jwtScrectKey:'yangpengkl',
expiresIn:'10h'//指定token有效期
}
3.创建user登录/注册路由
const express=require("express");
const router=express.Router();
//导入用户路由处理函数
const userHandel= require("../router_handler/user");
//1.导入用户验证的中间件
const express_jio=require("@escook/express-joi");
//2.导入需要的验证规则对象
const {reg_login_schems}=require("../schems/user")//得到对象中的一个属性 {reg_login_schems}
//注册
router.post("/reguser",express_jio(reg_login_schems),userHandel.regUser)//相当于类
//登录
router.post("/login",express_jio(reg_login_schems),userHandel.login)//验证通过之后才会继续调用userHandel.login
//获取用户名,头像,作品,用于展示
// router.get("/get_other_info",userHandel.getSomeInfo)
module.exports=router
4.创建登录/注册路由处理函数
//处理user请求
//导入数据库操作模块
const db=require("../db/index")
//导入加密密码模块
const bcryptjs=require("bcryptjs");
const { off } = require("../db/index");
//导入生成token的包
const jwt=require("jsonwebtoken");
//导入秘钥
const config=require("../config");
//导入文件回传模块
const fs=require("fs");
const { url } = require("inspector");
const router = require("../router/user");
//注册新用户的处理函数
exports.regUser=(req,res)=>{
//获取用户提交信息
const userinfo=req.body
//检验表单有无空数据
// if(!userinfo.userName || !userinfo.password){
// return res.send({
// status:1,
// message:"注册失败用户名或密码为空"
// })
// }
//1.定义sql语句查询,查询用户名或邮箱是否存在
const sql="select * from ev_user where username=?"
//检查邮箱是否已经存在
const sql_byEmail="select * from ev_user where email=?"
db.query(sql,userinfo.userName,function(err,results){
// if(err){
// return res.send({status:1,message:err.message})//执行sql失败
// }
//执行成功,判断用户名是否被占用
if(results.length>0){
return res.send({status:1,message:"用户名已存在!"});
}else if(results.length <=0){
db.query(sql_byEmail,userinfo.email,(err2,result2)=>{
// if(err2){
// return res.send({status:1,message:err2.message})
// }
if(result2.length>0){
return res.send({status:1,message:"邮箱已被使用!"});
}
//用户名邮箱都可以使用
//1.对密码加密,再存入数据库(使用bcryptjs包)先安装 npm i bcryptjs@2.4.3
//2.调用bcryptjs.hashSync() 加密
userinfo.password= bcryptjs.hashSync(userinfo.password,10)
// console.log(userinfo.password);//(明文密码,随机盐长度)
//插入新用户
const sql_into="insert into ev_user set?";
db.query(sql_into,{username:userinfo.userName,passwor:userinfo.password,email:userinfo.email},(err,result)=>{
if(err){
return res.send({status:1,message:err.message})
}
//判断影响行数是否为一,不为一则失败
if(result.affectedRows !=1){
return res.send({status:1,message:"注册用户失败,稍后再试"});
}
res.send({status:0,message:"注册成功!"})
});
})
}
})
}
//登录的处理函数
exports.login=(req,res)=>{
//接收表单数据
const userinfo=req.body;
//1.定义sql
const sql="select * from ev_user where username=?"
//2.执行sql
db.query(sql,userinfo.userName,(err,result)=>{
//执行失败
if(err){
return res.cc(err);
}
//没查到用户数据
if(result.length !==1){
return res.cc("登录失败,没有此用户!");
}
//判断密码是否正确,调用bcryptjs.compareSync()
const comperResult= bcryptjs.compareSync(userinfo.password,result[0].passwor);
if(!comperResult){
return res.cc("登录失败,密码错误");
}
//在服务端生成token字符串(生成token时要剔除敏感信息,比如密码等)
//利用es6的高级语法(展开运算符)清空 const user={...result[0],password:'',user_pic:''}
const user={...result[0],passwor:'',email:'',user_pic:''};
//加密用户信息生成token
const tokenStr=jwt.sign(user,config.jwtScrectKey,{expiresIn:config.expiresIn});
// console.log(tokenStr);
//将token响应客户端
res.send({
status:0,
message:"登陆成功",
token:'Bearer '+tokenStr,//加上前缀方便客户端使用(前缀是固定写法)
});
})
}
创建用户信息操作接口
1.创建获取信息,更改信息路由
const express= require("express");
const router= express.Router();
//导入验证数据的中间件
const expressjoi=require("@escook/express-joi");
//导入验证更新用户信息的规则
const {update_userinfo_schema,update_password_schema,update_avatar_schema}=require("../schems/user");
//挂载路由
//导入路由处理函数模块
const uerinfo_handle=require("../router_handler/userinfo");
//获取用户信息路由
router.get('/userinfo',uerinfo_handle.getUserinfo);
//更新用户数据路由
router.post('/userinfo',expressjoi(update_userinfo_schema),uerinfo_handle.updateUserInfo);
//更新密码的路由
router.post('/updatapwd',expressjoi(update_password_schema),uerinfo_handle.update_password);
//更换头像的路由
router.post('/update/avatar',expressjoi(update_avatar_schema),uerinfo_handle.updateAvatar);
module.exports=router;
2.创建操作用户信息路由处理函数
//导入大数据库操作模块
const db=require("../db/index");
//导入比较加密密码的模块
const bcryptjs= require("bcryptjs");
//获取用户信息的路由模块,处理函数
exports.getUserinfo=(req,res)=>{
//定义sql
const sql="select id,username,nickName,email,user_pic from ev_user where id=?"
//调用db.qurey()查询
db.query(sql,req.user.id,(err,result)=>{
if(err){
//执行sql失败
return res.cc(err);
}
//执行sql成功,但是查询结果不唯一
if(result.length !=1){
return res.cc("获取用户信息失败!");
}
//用户信息获取成功!
res.send({
status:0,
message:"获取用户信息成功!",
data:result[0] // 用户信息
})
});
}
//更新用户基本信息
exports.updateUserInfo=(req,res)=>{
//定义sql语句
const sql_update="update ev_user set ? where id=?"
//调用db.query()执行
db.query(sql_update,[req.body,req.body.id],(err,result)=>{
//执行sql失败
if(err){
return res.cc(err);
}
//执行成功但是影响行数不唯一
if(result.affectedRows !=1){
return res.cc("修改信息失败!")
}
res.send({
status:0,
message:"更新成功!"})
})
}
//更新密码
exports.update_password=(req,res)=>{
//定义sql语句
const sql_update_password_select="select * from ev_user where id=?"
//执行sql
db.query(sql_update_password_select,req.user.id,(err,result)=>{
//sql执行失败
if(err){
return res.cc(err);
}
//执行sql成功,但是影响行数不唯一
if(result.length !=1){
return res.cc("修改密码失败,用户不存在!");
}
//判断旧密码是否正确
let compareResult=bcryptjs.compareSync(req.body.oldPwd,result[0].passwor);//不能直接用==判断(加密了)
if(compareResult==false){
return res.cc("旧密码输入错误!");
}
//将密码加密后更新到数据库
const sql_update_password_update="update ev_user set passwor=? where id=?"
//对新密码加密
const newPwd=bcryptjs.hashSync(req.body.newPwd,10);
db.query(sql_update_password_update,[newPwd,req.user.id],(err,result)=>{
//sql执行失败
if(err){
return res.cc(err);
}
//执行sql成功但是影响行数不为一
if(result.affectedRows !=1){
return res.cc("修改密码失败!");
}
res.send("修改成功!");
})
});
}
//更换头像的处理函数
exports.updateAvatar=(req,res)=>{
//定义sql
const sql="update ev_user set user_pic=? where id=?"
//执行sql
db.query(sql,[req.body.avatar,req.user.id],(err,result)=>{
//注:avatar必须与验证规则里面的验证参数同名
//执行sql失败
if(err){
return res.cc(err);
}
//执行sql成功,但影响行数不为1
if(result.affectedRows !==1){
return res.cc("更新头像失败!");
}
//更新成功
res.send({
status:0,
message: "头像更新成功!"
});
});
}
创建博客处理接口
1.创建博客操作路由(创建,删除,获取)
const express= require('express')
const router=express.Router()
//导入路由处理函数
const articHandel=require('../router_handler/artic')
//引入验证规则
const schem_artic=require('../schems/artic')
//导入验证插件
const express_jio=require('@escook/express-joi')
//发布博客路由
router.post('/create_artic',express_jio(schem_artic.reg_artic),articHandel.save_artic)
//获取用户博客信息路由
router.get('/get_my_artic',articHandel.get_artic);
//获取一个种类的所有博客
router.get('/get_allartic_byclass',articHandel.grt_all_artic_byclass);
//删除博客路由
router.post('/delete_artic',articHandel.delete_artic)
module.exports=router;
2.创建博客路由处理函数
//导入数据库库操作模块
const db=require('../db/index')
//导入加密文章模块
const bcryptjs=require('bcryptjs')
//保存文章路由处理函数
exports.save_artic=(req,res)=>{
const artic_imfomation=req.body;
//对文章内容加密(应该不用加密)
// artic_imfomation.alias=bcryptjs.hashSync(artic_imfomation.alias,10);
//定义sql
const sql="INSERT INTO ev_article_cate set ? "
//执行sql
db.query(sql,{name:artic_imfomation.name,alias:artic_imfomation.alias,username:req.user.username,artic_title:artic_imfomation.artic_title},(err,result)=>{
//sql执行失败
if(err){
return res.cc(
{
std_flag:1,
message:err.message});
}
//sql执行成功,但是影响行数不为一
if(result.affectedRows !=1){
return res.cc("发布博客失败!");
}
res.send({
status:0,
message:'发布成功!'
})
})
}
//获取用户博客信息处理函数
exports.get_artic=function(req,res){
//定义查询博客信息的sql
const sql="SELECT * FROM ev_article_cate WHERE username=?"
//执行sql
db.query(sql,req.user.username,(err,result)=>{
if(err){
return res.cc(err.message);
}
res.send({
code:0,
msg:"成功",
data:result});
})
}
//删除博客的处理函数
exports.delete_artic=function(req,res){
//定义sql
const sql="DELETE FROM ev_article_cate WHERE id=?";
//执行sql失败
db.query(sql,req.body.Id,(err,result)=>{
if(err){
return res.cc(err.message);
}
if(result.affectedRows !=1){
return res.cc("删除失败!");
}
res.send({
status:0,
message:"删除成功!"
})
})
}
//获取一种博客的所有
exports.grt_all_artic_byclass=function(req,res){
//定义sql
const sql=`SELECT * FROM ev_article_cate WHERE NAME=?`
//执行sql
db.query(sql,req.query.name,(err,result)=>{
//sql执行失败
if(err){
return res.cc(err.message);
}
//sql执行成功
res.send({
status:0,
boke:result
})
})
}
总结
到此为止后端代码就基本完成,前端代码过于简单,笔者就不在这里赘述了,感兴趣的可以去笔者的github仓库自行下载,项目完全开源,如有不足的地方还希望大佬指正。 笔者的学习心得,对于此类新的事物我们应该抱有探索的精神去学习,喜欢的可以收藏加关注,笔者后续会继续更新Java项目,如果有技术上的错误,还望在评论区指出。