• Node教程——API接口开发(MangoDB+Express)


    git源码

    说明:源码已经全部上传到github,仓库地址: https://github.com/BM-laoli/Node-api-Design

    模块依赖关系图

    一、大纲

    • 大纲:
      关于架构,
      1. 首先我们的有一个app.js这个就是根路由起点,用来最初的打入口
        它的功能有:
        1.1 引入模块创建基础的网站服务器,
        1.2 导入bodyPasser,过滤还有处理我们的post请求
        1.3 导入数据库连接
        1.4 把路由开放出去

      2. 再来一个main.js它在我的route文件夹下,
        2.1 什么需啊哟再这里做二次拦截,再进行分配路由,
        2.2 引入两个逻辑处理模块,当请求发来的时候,如果不是login那么我们就需要验证token,
        2.3 如果访问的是login那么我们需要,发post数据,来处处理验证,然后拿token,
        2.4 如果有token,那么再来访问2.2这里的不是logi的其它路由路径,的校验就通过了,于是乎我们就能分配各种接口了

      3. 详细的讲解辅助,工具代码
        3.1 这里有我们在主线逻辑里需要的一些工具
        3.2 中间件Middleware,里面有两个工具,一个是生成token的一个校验token的
        3.3 在3.2中的功能 需要依赖model下的一个util下的jwt工具生成token生成的依据是两个密钥对
        3.4 我们还有两个工具,content 创建数据库的链接,create初始化数据还有开发数据操作对象

    二、展示 接口 API文档

    1.1 登录

    请求地址 请求方式
    /main/login POST
    参数名称 是否必选 参数说明
    email 邮箱
    password 密码
    {
    	"status": 200,
    	"msg": "登陆成功",
    	"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiNWU5MTIwMTViOWI0NmYzZmE4Y2MzMjUzIiwiZXhwIjoxNTg2OTUyMzk1LCJpYXQiOjE1ODY5NTA1OTV9.IsprCaQ_gZRh0BzS8SnAd0iJ27BOpOEb-ZGn0bTlwHVPTiYPK50wiEOL_0aAYINfNT_Mfvb726l3CpiHG9lsJ45m4eqhPeJz9TbAeQj8-ST3CAkYLrD0fhgRG9YiQ5kjVpygdR8NZEWEUV7ux-moyYe7wCoVzN9mbvAkFF3IYG0"
    }
    

    1.2 拿着token进行测试

    请求地址 请求方式
    /main/text get
    参数名称 是否必选 参数说明
    token 需要附上你的token。在请求头中
    该接口不需要传递参数
    {
    	"status": 200,
    	"msg": "登陆成功"
    }
    

    注意:以上就是Node写接口的最基础的内容。可以在这个内容上扩展更多的接口

    二、 app.js模块的详解

    app.js

    //引入模块,创建简单服务器
    const express = require('express');
    //引入路径处理模块
    const path = require('path');
    //引入注意使用第三方模块处理post
    const bodyPaser = require('body-parser')
    
    // 引入数据库连接 不需要返回值
    require('./model/content')
    
    //创建服务器
    const app = express();
    
    /*//初始化数据库,注意reque的时候 会自动执行引入的js文件
    require('./model/create')
    */
    
    
    //前往小心这个要放在所有的use前面,解析我们的post请求数据
    app.use(bodyPaser.urlencoded({ extended: false }));
    //处理静态资源路径
    const DataPath = path.join(__dirname, 'public');
    //这个我们后面是有用的,用来操作媒体数据,最重要的就是这个路径还有这个静态资源对象
    const StaticUtils = express.static(path.join(__dirname, 'public'));
    
    
    //拿到路由操作对象
    const main = require('./route/mian/main');
    //开放接口路径
    //拦截请求开始匹配路由  
    app.use('/dataPath', (req, res) => {
        res.status(200).send(JSON.stringify({ 'dataPath': DataPath }))
    })
    app.use(StaticUtils);
    app.use('/main', main)
    
    
    //监听端口
    app.listen(3000)
    console.log('服务器开启');
    

    三、 main.js二次路由拦截模块

    main.js

    const express = require('express')
    
    //业务逻辑
    const guard = require('../../Middleware/loginGuard')
    const loginPash = require('../../Middleware/loginPash')
    
    
    
    
    //创建路由对象
    const admin = express.Router()
        //验证token
    admin.use(loginPash)
    
    //登录路由处理
    admin.post('/login', guard)
    
    //首先要验证,然后才是放行到对应的路由接口里面取
    admin.get('/text', require('./API/home/index'))
    
    module.exports = admin;
    
    • 携带的一个简单的测试模块(后续可以仿照这样的模式,写更多的API接口功能模块)
      home/index.js
    module.exports = async(req, res) => {
        res.send({ status: 200, msg: '登陆成功', });
    }
    

    四、 辅助工具(重点!!!)

    • 数据库连接,设计还有初始化创建模块
      model/content.js && model/create.js
    
    const mongoose = require('mongoose');
    
    mongoose.connect('mongodb://localhost/blog')
        .then(() => {
            console.log('数据库连接成功');
        })
        .catch((erro) => {
            console.log(erro);
        })
    
    
     const mongoose = require('mongoose');
    
     const bcrypt = require('bcrypt')
    
    
     /*使用MongDB数据库,设计数据第一步(代码角度分析)
     1.设定规则,Schema构造器,Schema的构造函数就是规则,注意规则是一系列对象
     2.创建数据
     */
    
     const userSchema = new mongoose.Schema({
         username: {
             type: String,
             required: true,
             min: 2,
             max: 10
         },
         email: {
             type: String,
             //注意 我们的邮箱凭据是用户登录的令牌,我们需要指定他为唯一的
             unique: true, //这个时候,在插入的时候如果有重复的就给你抱一个错误
         },
         password: {
             type: String,
             required: true,
         },
         role: { //需要说明一下,这个地方角色,我们硬性规定,超级管理员是admin普通用户是normal,为什么不用01?因为这里string了
             type: String,
             required: true
         },
         state: {
             //我们使用O1状态来设计这个数据字段,默认值是0。0是启用状态
             type: Number,
             default: 0
         }
     })
    
     //使用规则创建集合,
     //一定要注意,我们的User就是代表了我目前最user数据集合,后期的增删改查我们都需要用到这个东西,所有我们暴露出去给路由业务使用
     const User = mongoose.model('User', userSchema)
    
    
     //我们再来复习一下,如何用同步的方式获取异步的api结果?使用async 还有awit就可以
    
     async function createUser() {
         const salt = await bcrypt.genSalt(10)
         const pass = await bcrypt.hash('123456', salt)
         const user = await User.create({
             username: 'lli',
             email: '18376621755@163.com',
             password: pass,
             role: 'admin',
             state: 0,
         })
     }
    
    
     //  createUser()
    
    
    
    
     /* //  初始化用户,注意我们的create返回的是一个promies
       User.create({
         username: 'bmlaoli',
         email: '861795660@qq.com',
    
         password: '123456',
    
         role: 'admin',
         state: 0,
     })
     .then(() => {
         console.log('用户创建成功');
     })
     .catch((error) => {
         console.log('用户创建失败');
     })*/
    
    
    
    
    
    
    
    
     //注意啊,es6中如果键值对的名字是一样的就可以省略值。由于我们后期还会做更多的数据集合,于是乎我这里需要暴露一个对象出去
     module.exports = {
         User
     }
    
    • 另一个重要的工具模块,用来创建token 根据就是文件夹下的两个密钥对文件
      jwt.js
    
    
    /*
     *描述:以下是jwt工具,生成用于访问验证的token 
     */
    
    // 引入模块依赖
    const fs = require('fs');
    const path = require('path');
    const jwt = require('jsonwebtoken');
    // 创建 token 类
    class Jwt {
        constructor(data) {
                this.data = data;
            }
            //生成token
        generateToken() {
            let data = this.data;
            let created = Math.floor(Date.now() / 1000);
            let cert = fs.readFileSync(path.join(__dirname, '../../pem/private_key.pem')); //私钥 可以自己生成,注意这里要使用 密钥对,请求百度下载,两对密钥对
            let token = jwt.sign({
                data,
                exp: created + 60 * 30,
            }, cert, { algorithm: 'RS256' });
            return token;
        }
    
        // 校验token
        verifyToken() {
            let token = this.data;
            let cert = fs.readFileSync(path.join(__dirname, '../../pem/public_key.pem')); //公钥 可以自己生成
            let res;
            try {
                let result = jwt.verify(token, cert, { algorithms: ['RS256'] }) || {};
                let { exp = 0 } = result, current = Math.floor(Date.now() / 1000);
                if (current <= exp) {
                    res = result.data || {};
                }
            } catch (e) {
                res = 'err';
            }
            return res;
        }
    }
    
    module.exports = Jwt;
    
    
    • 接下里是两个具体的token校验功能模块
      我把它们都放到了middleware文件夹下
      loginGuard.js && loginPash.js
    const JwtUtil = require('../model/util/jwt')
    const { User } = require('../model/create')
    const bcrypt = require('bcrypt')
    
    
    const guard = async(req, res, next) => {
        //注意使用第三方模块处理post
        //进图具体的业务逻辑,解构出来我们需要的东西
        const { email, password, _id } = req.body; //注意啊,由于我们的中间件处理的请求于是乎我们的req身上就有这个处理的所有数据了,这个之前有说过
    
        console.log(req.body);
    
        if (email.trim().length == 0 || password.trim().length == 0) {
            res.status(400).send(
                    JSON.stringify({ message: '邮箱或密码错误' })
                ) //注意send自动把状态码设置成了200,所以你需要改一下
            return
        }
    
    
        //如果用户存在就先找到这个用户额信息,注意这里的这个异步await
        let user = await User.findOne({ email });
        //这里的user就是指向当前查询出来的数据文档对象
    
        if (user) {
            //比对操作,第一个参数是一个明文密码,第二个参数我们查询出来的加密密码 ,方法返回一个Boolean值,对比成功就是true,异步api可以直接加await
            const isValid = await bcrypt.compare(password, user.password)
            if (isValid) {
                //用户校验成功,添加tooken
                // 登陆成功,添加token验证
                let _id = user._id.toString();
                // 将用户id传入并生成token
                let jwt = new JwtUtil(_id);
                let token = jwt.generateToken();
                // 将 token 返回给客户端
                res.send({ status: 200, msg: '登陆成功', token: token });
                //校验成功就
                next()
            } else {
                res.status(400).send(
                    JSON.stringify({ message: '邮箱或密码错误' })
                )
            }
    
        } else {
            res.status(400).send(
                JSON.stringify({ message: '邮箱或密码错误' })
            )
        }
    }
    
    module.exports = guard
    
    

    loginPash.js

    
    const JwtUtil = require('../model/util/jwt')
    
    
    
    
    
    //验证token
    const loginPash = function(req, res, next) {
        // 我这里知识把登陆和注册请求去掉了,其他的多有请求都需要进行token校验 
        if (req.url != '/login?') {
            let token = req.headers.token;
            let jwt = new JwtUtil(token);
            let result = jwt.verifyToken();
            // 如果考验通过就next,否则就返回登陆信息不正确
            if (result == 'err') {
                console.log(result);
                console.log(req.url);
                res.send({ status: 403, msg: '登录已过期,请重新登录' });
                // res.render('login.html');
            } else {
                next();
            }
        } else {
            next();
        }
    };
    
    
    
    module.exports = loginPash;
    
    

    五、 最后我们梳理一下,这些模块之间的关系

    其实这套接口下来,我比较菜鸡,用了两天的工作之余的时间,由于我没怎么接触接口的设计,对于后台的一些设计模式还有理念不是特别懂,走了很多弯路,以上的是我写了三次笔记,长达2000多行子,反复的修改,才成型,希望大佬高抬贵手指点迷津,也希望,这篇入门的文档,能给你带来收获,接下里,我就用这里的模型,开发更多的接口。敬请期待。

    • 各个模块的引用关系,用例图

  • 相关阅读:
    python疑难问题---5、二维列表初始化
    python疑难问题---4、python文件读写
    心得体悟帖---200701(你以为后面比当下好,其实还真的不一定)
    心得体悟帖---200701(《隐秘的角落》成熟的孩子小白)
    div垂直居中 css div盒子上下垂直居中
    iOS 7 新版微信 URL 不支持跳转 App Store 的解决方案
    HTML5 LocalStorage 本地存储
    WDCP是什么 关于WDCP的详细介绍
    前端框架用哪个好
    GWT 实现文件上传和下载
  • 原文地址:https://www.cnblogs.com/BM-laoli/p/12708342.html
Copyright © 2020-2023  润新知