基础使用
创建项目 安装express
npm init 创建项目
然后安装如下命令:
npm install express
type nul>app.js windows命令创建入口文件
创建一个服务
代码如下:
const express=require("express");
const app=express();
app.get("/",function(req,res){
res.send("hello world");
})
app.listen(3000,function(){
console.log("server running http://localhost:3000");
});
node.js时时刷新
安装如下包
npm -g install nodemon
nodemon app.js 则会时时刷新
启动端口
//端口号,环境变量是否存在
const PORT=process.env.PORT || 8801;
app.listen(PORT,function(){
console.log("server running http://localhost:"+PORT);
});
常用中间件
解析请求体中间件
这是express自带的中间件
app.use(express.json());//解析请求体、得到请求体是json的格式
日志中间件
npm install morgan
const morgan=require("morgan");
app.use(morgan("dev"));//日志输出,开发模式
跨域中间件
npm install cors
const cors=require("cors");
app.use(cors());//这是允许跨域的中间件
如何去判断跨域中间件是否生效:
允许跨域,在请求头里边会看到如下
如果没有不允许跨域的话:
在请求头里边是找不到上边的那一段内容的。
能不能跨域都是在请求后才能够查看的,在请求头里边查看
路由设计
把路由设计到其他的页面
另外一个页面:
const express=require("express");
const router=express.Router();
router.get("/",(req,res)=>{
console.log(res);
res.send("hello word get");
});
router.post("/",(req,res)=>{
console.log(res);
res.send("hello word post");
});
//将路由返回
module.exports=router;
在入口文件下app.js查看:
//挂载路由
app.use("/api",router);
分文件管理路由
例如user.js 是管理user相关的路由的
代码如下:
const express=require("express");
const router=express.Router();
router.get("/user/login",(req,res,nuxt)=>{
res.send("hello /user/login");
nuxt();
})
module.exports=router;
上边是user.js 文件,然后把它引入index.js 这个路由的总文件
const express=require("express");
const router=express.Router();
const user=require("./user");
//把用户路由绑定在当前router对象下
router.use(user);
//将路由返回
module.exports=router;
挂载的方式两种:
router.use(user);
router.use("/profile",profile);
控制器
我们把每个路由处理的业务逻辑都放入到controller的目录下,目录下的文件按照路由的文件进行匹配。把路由的方法放入到这个控制器里边。
如下代码:
//注册
exports.register=async (req,res,next)=>{
try{
res.send("hello /users");
}catch(err){
next(err);
}
}
//登录
exports.login= (req,res,next)=>{
try{
res.send("hello /users/login");
}catch(err){
next(err);
}
};
//获取用户的信息
exports.getUserMessage=async (req,res,next)=>{
try{
res.send("hello /user");
}catch(err){
next(err);
}
};
//更新用户的信息
exports.updateUserMessage=async (req,res,next)=>{
try{
res.send("put /user");
}catch(err){
next(err);
}
};
上边控制器的使用代码如下:
//用户注册
router.post("/users",userCtrl.register)
//用户登陆
router.post("/users/login",userCtrl.login)
//获取当前登录用户
router.get("/user",userCtrl.getUserMessage)
//更新当前登录用户
router.put("/user",userCtrl.updateUserMessage);
post请求express接收不到数据
需要在app.js 在路由运行的上边,加上如下代码:
//对post请求提供一个请求体的解析
app.use(express.urlencoded({
extended:true
}));
postman统一管理api接口测试
建立一个集合,在集合里边建立请求,如下截图:
链接数据库mongooDB
安装mongodb
我这里使用的是linux上宝塔安装mongodb
安装好mongodb后需要注意的三个地方
指定数据库存储位置:
mongo --dbpath /www/server/mongodb/data
关闭防火墙
stystemctl stop firewalld.service
修改mongodb配置文件
如下截图,默认是 127.0.0.1 那么只能是本机访问,则修改成0.0.0.0 则远程也是可以链接的
安装mongoose
npm install mongoose
链接mongodbd的相关操作
如下代码:
const mongoose =require("mongoose");
let statusDb=mongoose.connect("mongodb:xxxxxx/expressdemo");//返回一个待定的状态,这个状态作用不大
let db = mongoose.connection;//获取数据库的链接状况
db.on('error', console.error.bind(console, 'connection error:'));//链接失败
db.once('open', function() {//成功则执行回调函数
// we're connected!
console.log("database linked success");
});
//创建一个模型,一个模型对应一个数据表,表名和表的字段
const User=mongoose.model("User",{name:String,password:String,email:String});
//创建一个用户
const user1=new User({name:"zhangsan",password:"123456",email:"11@qq.com"});
//用户提交数据后,返回一个promise
user1.save().then(res=>{
console.log("res::::>>>>>",res);
});
把相关的信息放入到配置文件里边
database.config.js
创建模型的字段
const mongoose= require("mongoose");
const userSchema=new mongoose.Schema({
username:{
type:String,
require:true
},
password:{
type:String,
require:true
},
email:{
type:String,
require:true
},
bio:{
type:String,
default:null
},
image:{
type:String,
default:null
},
createAt:{
type:Date,
default:Date.now
},
createAt:{
type:Date,
default:Date.now
}
});
module.exports=userSchema;
模型中公共字段的处理
公共模型的文件:
module.exports={
createAt:{
type:Date,
default:Date.now
},
updateAt:{
type:Date,
default:Date.now
}
}
模型中使用公用字段:
const mongoose= require("mongoose");
const basemodel=require("./basemodel");
const userSchema=new mongoose.Schema({
...basemodel,
username:{
type:String,
require:true
},
password:{
type:String,
require:true
},
email:{
type:String,
require:true
},
bio:{
type:String,
default:null
},
image:{
type:String,
default:null
}
});
module.exports=userSchema;
关于数据验证
需要使用express-validator
官方:express-validator.github.io/docs/
相关的使用如下边
安装
npm install express-validator
express-validator的验证使用
const {User}=require("./../model/index");
const {body,validationResult} =require("express-validator");
//用户注册,在路由执行之前是加入中间件的
router.post("/users",
[//1.规则
body("username").notEmpty().withMessage("用户名不能够为空"),//不能够为空
body("password").notEmpty().withMessage("密码不能够为空"),
body("email").notEmpty().withMessage("邮箱不能为空")
.bail()//如果上边条件不满足,则在这里中断
.isEmail().withMessage("邮箱格式不正确")
.custom(value=>{//自定义规则
//数据库查看邮箱是否重名
let user= User.findOne({ email:value });
if(user){//如果不为空,说明邮箱已经存在
return Promise.reject("邮箱已经存在");
}
})
],
(req,res,next)=>{//2.判断规则后返回的结果
const errors=validationResult(req);//上边规则说明是body
if(!errors.isEmpty()){//如果不为空,那么说明有错误,那么就返回给前端
return res.status(400).json({"errors:":errors.array()});
}
next();//3.继续执行下一步
},
userCtrl.register)
提取express-validator使用规则和结果
- 新建一个中间件,用于处理验证的结果
这个是对返回结果的处理的代码:
const {validationResult}=require("express-validator");
module.exports=validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
};
};
- 第一步是用于对规则进行的结果处理
那么我们第二步是把结果拿出来在第二步中进行使用。
const validate=require("./../middleware/validate");
const {body}=require("express-validator");
const {User}=require("./../model");
exports.register=validate([
body("username").notEmpty().withMessage("用户名不能够为空"),//不能够为空
body("password").notEmpty().withMessage("密码不能够为空"),
body("email").notEmpty().withMessage("邮箱不能为空")
.bail()//如果上边条件不满足,则在这里中断
.isEmail().withMessage("邮箱格式不正确")
.custom(async value=>{//自定义规则
//数据库查看邮箱是否重名
let user=await User.findOne({ email:value });
console.log("useruser::::>>>>",user);
if(user){//如果不为空,说明邮箱已经存在
return Promise.reject("邮箱已经存在");
}
})
]);
-
第三步 就是把上述两步组合成的中间件,在下边使用
//用户注册,在路由执行之前是加入中间件的
router.post(
"/users",
userValidate.register,
userCtrl.register)
密码加密
md5加密文件的代码如下:
//加密的包,不仅仅是md5,还有很多其他的加密包
const crypto=require("crypto");
module.exports=value=>{
return crypto.createHash("md5")
.update("laohu"+value)
.digest("hex");//使用哪一种方式进行加密
}
对密码加密的几个渠道
第一种渠道是:在控制器里边提交数据时,进行加密。缺点是每次只要密码加密就都需要这个操作,会产生大量的重复操作。
第二种是:模型里加密,只要是操作密码就会动用这个模型,自动对密码加密。解决大量重复操作。
模型内处理的加密和返回查询数据
在模型里边导入md5包:
const md5=require("./../utils/md5");
然后给密码做如下的设置:
password:{
type:String,
require:true,
set:value=> md5(value),
},
如过我们在查询数据时不想返回密码。但是使用模型返回的是模型对象也就是mongoose对象,mongoose对象转化成json对象的方法是:mongoose.toJSON()方法
但是我们是可以通过设置模型来不让数据库查询时返回密码的,方式如下:
password:{
type:String,
require:true,
set:value=> md5(value),
select:false
},
登录验证
需要验证多个项,且每个项都是要有顺序的:
代码如下:
//通过顺序进行验证,前面验证通过之后才会验证后边的,所以是多个validate,需要用到数组的方式。
exports.login=[ validate([//验证用户名和密码是否为空 body("username").notEmpty().withMessage("邮箱不能够为空"), body("password").notEmpty().withMessage("密码不能够为空") ]),
validate([//验证用户名是否存在,数据库一般是后验证
body("username").custom(async (username,{req})=>{ //解构对象
const user=await User.findOne({username})
.select(["username","password","image","bio","_id","email"]);//返回想要的字段
if(!user){
return Promise.reject("用户名不存在!!!");
}
//把返回的用户数据对象绑定在req上
req.user=user;
})
]),
validate([//如果用户名存在,那么就要验证密码是否相等了
body("password").custom(async (password,{req})=>{
if(md5(password)!=req.user.password){//如果相等则表示密码正确
return Promise.reject("密码不正确");
}
})
])
];
JWT
jwt.io
jwt默认是不加密的。通过上边网站是可以把信息转换成明文的。
生成的token可以放入到localstorage里边,也可以放入到cookie,不过一般最好是放入到header中的authorization会更好。
jwt的验证和使用
//签名,这里的第一个参数是数据、第二个参数是签名用的字符串、如果是异步第三个参数是签名后的结果状态,结果正确返回token
token=jwt.sign({foo:"1234556"},"laohu");
//验证
//验证签名,第一个参数是被验证的token,第二个参数是签名用的字符串,第三个参数是回调用于此次验证的一个结果
const data=jwt.verify(token,"laohu");//会得出一个结果是数据和iat。iat表示的是时间
登陆时生成token并且返回给前端
try{
let token="";
//验证过了之后就会生成token
let user=req.user.toJSON();
//生成token
token=await sign({_id:user._id},jwtSecret);
// user.password=undefined;
delete user.password;
console.log("user:::>>>>",user);
res.status(200).json({...user,token});
// res.send("hello loggin success");
}catch(err){
next(err);
}
jwt 使用中间件 统一验证
以查找当前用户信息的代码如下:
const {verify}=require("./../utils/jwt");
const {jwtSecret}=require("./../config/db.config");
const {User}=require("./../model");
//中间件,用于验证是否携带token
module.exports=async (req,res,next)=>{
let token=req.header("authorization");
token=token?token.split(" ")[1]:null;
if(!token){//如果为null说明没有token
return res.status(401).json({status:0,"msg":"没有权限"});
}
try{
//验证token
let user=await verify(token,jwtSecret)
console.log(user);
//user会携带一个id,数据库如果存在这个id,那么token可以过,且将查找出来的对象挂载到req上
req.user=await User.findById(user._id);
next();
}catch(err){
res.status(401).json({status:0,"error":err});
}
console.log(token);
}
token时间的设置
默认的token是长期有效的。
在生成token时,设置token时间。这个时间是否过去则在验证token的时候进行验证。
token=await sign({_id:user._id},jwtSecret,{expiresIn:60*60*24*30});
部署
宝塔 安装 pm2管理器
自带 node.js nvm pm等相关插件