本文主要分享 通过使用express-generator脚手架 来配置用户接口的响应
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var logger = require('morgan');
let multer = require('multer');
// var indexRouter = require('./routes/index');
// var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views')); //指明项目里面模板引擎的位置放在views目录
app.set('view engine', 'ejs'); //当前项目可以通过res.render来去渲染一个ejs模板
//配置控制台打印日志的中间件
app.use(logger('dev'));
//中间件的安装 body-parser req.body获取到非地址栏里面的数据
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
//multer中间件的配置(1.可以获取上传图片req.files 2.req.body可以获取非地址栏的数据)
//通过multer实现上传图片的不同分发目录
// let upload = multer({dest:path.join(__dirname,"public","upload")})
var storage = multer.diskStorage({
destination: function (req, file, cb) {
//如果接口中存在reg或者user,那么我就上传到/upload/user文件夹中
if(req.url.indexOf("user")!==-1 || req.url.indexOf("reg")!==-1){
cb(null, path.join(__dirname,"public","upload","user"))
}else if(req.url.indexOf("banner") !== -1){
cb(null, path.join(__dirname,"public","upload","banner"))
}else{
cb(null, path.join(__dirname,"public","upload","product"))
}
}
})
let upload = multer({ storage: storage })
app.use(upload.any())
//多目录的静态资源托管
app.use(express.static(path.join(__dirname, 'public',"template")));
app.use("/admin",express.static(path.join(__dirname, 'public',"admin")));
app.use(express.static(path.join(__dirname,"public")))
// 用户的接口的响应
app.all("/api/*",require("./routes/api/params")) //处理api下发出的所有的接口的公共参数
app.use('/api/reg', require('./routes/api/reg'));
app.use('/api/login', require('./routes/api/login'));
app.use('/api/goods', require('./routes/api/goods'));
app.use('/api/user', require('./routes/api/user'));
//app.use('/api/logout', require('./routes/api/logout')); //session delete req.session[key]
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
//如果上面的接口不存在或者静态资源不存在,就会走下面的错误处理.....
if(req.url.includes("/api")){ //说明是用户接口不存在
res.send({err:1,msg:"用户端接口不存在..."})
}else if(req.url.includes("/admin")){ //说明是管理端接口不存在
res.render('error')
}else{ //静态资源不存在
res.sendFile(path.join(__dirname,"public","template","404.html"))
}
});
module.exports = app;
在routes 目录下创建 api文件夹 ,分别新建params.js
,reg.js
,login.js
,goods.js
,user.js
用来配置接口的响应。
let jwt = require("../../utils/jwt")
module.exports = (req,res,next)=>{
//处理公共参数(放在地址栏里面进行传递)
req.query._page = req.query._page ? req.query._page - 1 : require("../../config/global")._page
req.query._limit = req.query._limit ? req.query._limit - 0 : require("../../config/global")._limit
req.query.q = req.query.q ? req.query.q : require("../../config/global").q
req.query._sort = req.query._sort ? req.query._sort : require("../../config/global")._sort
//处理公共的授权业务
//如果前端接口包含 login/reg/logout,那么就可以不需要携带token /api/login
if( /login|logout|reg/.test(req.url) ){
//next必须要放行
next()
}else{ //除此之外的接口必须要携带token才能进行数据获取 /api/goods/home
//获取前端传递来的token
let token = req.headers.token || req.body.token || req.query.token
//校验前端传递来的token
jwt.verify(token).then(decode=>{
req.query.decode = decode //如果后续的接口需要数据的话,方便获取到 {username,_id}
next()
}).catch(message=>res.send({err:1,msg:"token验证失效或者未登录..."+message}))
}
}
var express = require('express');
var router = express.Router();
var fs = require("fs")
var pathLib = require("path")
var mongodb = require("../../utils/mongodb")
var bcrypt = require("../../utils/bcrypt")
/*
reg代表注册逻辑 往数据库里面进行数据的插入
restful接口规范:
插入数据 post方式
删除数据 delete方式
修改数据 patch/put方式 put是覆盖性修改 patch代表局部修改
查询数据 get方式
*/
// /api/reg ===> post请求 (username,password)
router.post('/', function(req, res, next) {
//1.获取前端传递来的username/password/nickname/icon等
let {username,password,nickname} = req.body;
//2.对于前端必传参数username、password的验证
if(!username || !password){
res.send({err:1,msg:"用户名或者密码是必传参数!"})
return
}
//3.整理其他的需要入库的参数(nickname/icon)
nickname = nickname || "系统生成的默认昵称"
let follow = 0 //关注数
let fans = 0 //粉丝数
let time = Date.now() //服务器生成注册时间
let icon = "/upload/default.jpg" //用户的默认头像
//判断用户是否上传了头像 multer中间件如果有用,req.files不在是undefined
if(req.files && req.files.length>0){ //代表用户上传头像了
//给上传好的图片添加后缀名
//文件改名
fs.renameSync(req.files[0].path,req.files[0].path + pathLib.parse(req.files[0].originalname).ext)
//覆盖掉默认头像
icon = "/upload/user/"+req.files[0].filename + pathLib.parse(req.files[0].originalname).ext
}
//4.校验用户是否在数据库存在(username的唯一性)
mongodb.open({
collectionName:"user"
}).then(({collection,client})=>{
//根据注册的用户名从库里面执行查询操作
collection.find({username}).toArray((err,result)=>{
if(err){
res.send({err:1,msg:"集合操作失败..."})
client.close()
}else{
if(result.length ===0 ){// 4-1 如果不存在的话,直接入库操作
//说明需要注册的用户在数据库里面不存在,进行入库操作
//密码需要进行加密
password = bcrypt.hashSync(password)
//console.log(username,password,nickname,fans,follow,time,icon)
//入库
collection.insertOne({
username,password,nickname,fans,follow,time,icon
},(err,result)=>{
if(!err){
//插入后的信息中不需要给用户返回用户名与密码
delete result.ops[0].username
delete result.ops[0].password
res.send({
err:0,
msg:"恭喜您,注册成功!",
data:result.ops[0]
})
}else{
res.send({err:1,msg:"入库失败..."})
}
client.close()
})
}else{ // 4-2 用户存在提示用户已存在信息 /upload/user/XXXXX.png
if(icon.indexOf("default")===-1){ //用户上传的头像路径不包含default
// fs.unlinkSync("./public"+icon)
fs.unlinkSync(pathLib.join(__dirname,"../../public",icon))
}
res.send({err:1,msg:"此用户已经存在,注册失败!"})
client.close()
}
}
})
}).catch(err=>{
console.log({err:1,msg:"数据库连接失败...."})
})
});
module.exports = router;
var express = require('express');
var router = express.Router();
var mongodb = require("../../utils/mongodb")
var bcrypt = require("../../utils/bcrypt")
var jwt = require("../../utils/jwt")
/* GET home page. */
router.post('/', function(req, res, next) {
//1.获取前端传递来的用户名与密码
let {username,password} = req.body
//2.验证必传参数
if(!username || !password){
res.send({
err:1,
msg:"用户名与密码为必传参数..."
})
return
}
//3.需要查询数据库
mongodb.open({
collectionName:"user"
}).then(({collection,client})=>{
//进行查询操作
collection.find({username}).toArray((err,result)=>{
if(err){
res.send({err:1,msg:"集合操作失败..."})
client.close()
}else{
if(result.length>0){
//3-1 代表用户在数据库是存在的 ==> 登录的密码与数据库里面的密码进行校验 ==> 登录成功(token)
let flag = bcrypt.compareSync(password,result[0].password)
if(flag){ //说明密码输入正确
//生成token令牌
let token = jwt.sign({username,_id:result[0]._id})
delete result[0].username
delete result[0].password
res.send({
err:0,
msg:"恭喜您,登录成功!",
token,
data:result[0]
})
}else{
res.send({err:1,msg:"用户名或者密码输入有误..."})
}
client.close()
}else{ //3-2 用户在数据库里面不存在
res.send({err:1,msg:"此用户不存在,请注册!"})
client.close()
}
}
})
}).catch(err=>{
res.send({err:1,msg:"数据库连接失败..."})
})
});
module.exports = router;
var express = require('express');
var router = express.Router();
var mongodb = require("../../utils/mongodb")
//列表查询商品 /api/goods/home就会进来
router.get("/:goodsName",(req,res,next)=>{
//判断是否有_id参数
if(req.query._id){
res.redirect(`/api/goods/${req.params.goodsName}/${req.query._id}`)
return;
}
// console.log("goods列表进入了...")
let collectionName = req.params.goodsName;
let {_page,_limit,_sort,q} = req.query
mongodb.findList({
collectionName,
_page,_limit,_sort,q
}).then(result=>{
res.send(result)
}).catch(err=>res.send(err))
})
//详情 /api/goods/home/224242342
router.get("/:goodsName/:_id",(req,res,next)=>{
let collectionName = req.params.goodsName;
let _id = req.params._id;
mongodb.findDetail({collectionName,_id}).then(result=>{
res.send(result)
}).catch(err=>res.send(err))
})
module.exports = router;
var express = require('express');
var router = express.Router();
var jwt = require("../../utils/jwt")
var bcrypt = require("../../utils/bcrypt")
var mongodb = require("../../utils/mongodb")
/* GET home page. */ // /api/user个人中心
router.get('/', function(req, res, next) {
//连接数据库
mongodb.open({
collectionName:"user"
}).then(({collection,client,ObjectId})=>{
//查询数据
collection.find({
username:req.query.decode.username, //因为app.all("/api/*") ===> 经过token认证后可以获取到username和_id
_id:ObjectId(req.query.decode._id)
}).toArray((err,result)=>{
if(err){
res.send({err:1,msg:"集合操作失败..."})
}else{
//判断用户是否存在
if(result.length>0){
//自动登录成功 需要返回数据给前端
delete result[0].username
delete result[0].password
res.send({
err:0,
msg:"自动登录成功...",
data:result[0]
})
}else{
res.send({err:1,msg:"自动登录失败..."})
}
}
client.close()
})
}).catch(err=>res.send({err:1,msg:"数据库连接失败..."}))
});
module.exports = router;
utils目录下 bcrypt.js(密码加密)
,jwt.js(token生成与校验)
,mongodb.js(mongodb链接库及查询)
//可以用来对前端传递来的密码进行加密
let bcrypt = require("bcrypt")
module.exports = {
//加密 const hash = bcrypt.hashSync(用户未加密的密码, salt);
hashSync:password=>{
return bcrypt.hashSync(password,10)
},
//解密校验 bcrypt.compareSync(用户未加密的密码, 用户加密之后的hash密码);
compareSync:(password,hash)=>bcrypt.compareSync(password,hash)
}
let jwt = require("jsonwebtoken")
module.exports = {
//生成签名
sign:({username,_id})=>{
return jwt.sign({username,_id},"MD5",{expiresIn:60*60*24})
},
//校验签名
verify:token=>{
return new Promise((resolve,reject)=>{
jwt.verify(token,"MD5",(err,decode)=>{
if(!err){
resolve(decode) //decode={username,_id}
}else{
reject(err.message)
}
})
})
}
}
let mongodb = require("mongodb")
let mongodCt = mongodb.MongoClient
let ObjectId = mongodb.ObjectId //把字符串转成ObjectId的对象类型
//链接库
/*
dbName:数据库名称 默认值就是student
collectionName:集合名字
url:链接的url地址
*/
let open = ({dbName='newsapp',collectionName,url="mongodb://127.0.0.1:27017"})=>{
return new Promise((resolve,reject)=>{
mongodCt.connect(url,{useUnifiedTopology: true},(err,client)=>{
if(err){
reject(err)
}else{
let db = client.db(dbName)
let collection = db.collection(collectionName)
resolve({collection,client,ObjectId})
}
})
})
}
//查询库集合列表数据
let findList = ({
collectionName,//集合名字
dbName='newsapp',//默认指明的数据库的名字
_page,_limit,_sort,q
})=>{
//生成检索条件
let rule = q ? {title:new RegExp(q,'g')} : {} //张三 张三丰
// let rule = q ? {username:eval('/'+q+"/")} : {}
return new Promise((resolve,reject)=>{
//链接数据库
open({dbName,collectionName})
.then(({collection,client})=>{
//查询列表
collection.find(rule,{
skip:_page*_limit,//跳过多少条数据
limit:_limit,//限定每一页的数量
sort:{[_sort]:1} //排序字段_sort 当一个变量作为key使用的时候,需要采用[]的语法
}).toArray((err,result)=>{
if(!err && result.length>0){
resolve({err:0,data:result})
}else{
resolve({err:1,msg:"查无数据..."})
}
//关闭资源
client.close()
})
})
.catch(err=>{ //链接数据库失败
reject({err:1,msg:"数据库链接失败...."})
})
})
}
//根据动态id获取详情数据
let findDetail = ({
dbName="newsapp",//默认查询的数据库名字
collectionName,//集合名字
_id=null //外面传入的_id
})=>{
return new Promise((resolve,reject)=>{
//链库操作
open({dbName,collectionName})
.then(({collection,client})=>{
//查询
if(_id.length === 24){
collection.find({_id:ObjectId(_id)}).toArray((err,data)=>{
//返回结果
if(!err && data.length>0){
resolve({err:0,data:data[0]}) //因为result是一个数组,里面仅仅包含一条数据
}else{
resolve({err:1,msg:"查询不到数据...."})
}
})
}else{
reject({err:1,msg:"id长度有误..."})
}
})
.catch(err=>reject({err:1,msg:"链接数据库失败...."}))
})
}
exports.open = open; //{open:函数}
exports.findList = findList;
exports.findDetail = findDetail;