express写接口

151 阅读9分钟

基础使用

创建项目 安装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/

别人总结:www.pipipi.net/4731.html

相关的使用如下边

安装

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等相关插件