草庐IT

复习 - node.js(接口案例)

Heymar-10 2023-03-28 原文

其实复习一次的作用真实太大了,真的,自从上次ajax开始其实就开始i有点懵懵懂懂的感觉,一直拖想到了node在去回顾一遍,这一次回去复习,ajax已经很熟练了,node之前搞不懂那些原理也顺清楚了好多,其实这次复习没有什么需要说的知识点,因为要说的前面都说过了,我来说一下这个做的一个大项目吧,这个项目真的,应该是我不熟练的愿意那边,就是用express写接口,用postman来测试,三个模块,三个数据库,基本上都在我的代码里面了,写的很详细步骤,用到的技术,基本上是用node的express模块,去写接口,然后中途中到了一些中间件,比如规定语义规则的joi,比如给密码解码加密的bcryptjs,我做了一天才做下来这一个案例

一个项目初试化,首先要创立一个单独的项目文件夹,然后终端npm init直接安装package.json,api.js接口文件,路由模块创立一个文件夹夹,路由函数又要分为一个模块,再把数据库创立好,基本就可以开始完成功能需求了,用后端node完成增删改查

项目文件分类:

 

 

 

1

接口文件

// 1.初始化
// 1.1创建项目
const express = require('express')
const app = express()
// 1.2配置跨域
const cors = require('cors')
app.use(cors())
// 1.3配置解析表单中间件
// 错误点:记住要有参数
app.use(express.urlencoded({extended : false}))

// 2.3因为后面处理函数用到了很多res.send所以封装为一个全局中间件,给res绑定一个函数,那后面的中间件路由都可以用到这个函数了
app.use((req, res, next) => {
    res.cc = function(err, status = 1) {
        res.send({
            status,
            msg : err instanceof Error ? err.message : err
        })
    }
    next()
})

// 2.4.6配置解析token的中间件
const expressJWT = require('express-jwt')
const secretKey = require('./secretKey')
app.use(expressJWT({secret : secretKey.secretKey, algorithms : ['HS256']}).unless({path : [/^\/api\//]}))

// 1.4.4导入路由模块
const routerUser = require('./router/user')
const Joi = require('joi')
const { expressjwt } = require('express-jwt')
const { path } = require('express/lib/application')
app.use('/api', routerUser)
// 3.1.1个人中心路由导入
const infoRouter = require('./router/userinfo')
app.use('/my', infoRouter)

// 4.1.2文章管理导入
const article = require('./router/acticle')
app.use('/my/article', article)

// 5.1.2发布文章路由导入
const cates = require('./router/cate')
app.use('/my/article', cates)

// 2.2.3定义规则joi的错误级别中间件
app.use((err, req, res, next) => {
    if (err instanceof Joi.ValidationError) return res.send(err.message)
    // 2.4.7增加jwt错误中间件
    if (err.name == 'UnauthorizedError') return res.cc('身份认证失败')
    return res.send('其他错误')
})


app.listen(80, () => {
    console.log('http://127.0.0.1');
})

2.

写好接口文件该去给路由创建模块

// 1.4初始化路由相关文件夹 不光要给路由分装一个模块 里面的处理函数也要有一个模块
const express = require('express')
const { append } = require('express/lib/response')
const router = express.Router()
// 1.4.2导入路由处理函数
const routerHandler = require('../router_handler/user')

// 注册
// 2.2.2导入joi验证输入进来的是否合法
const expressJOI = require('@escook/express-joi')
const {schema_user_info} = require('../schema/user')
router.post('/reguser',expressJOI(schema_user_info), routerHandler.getReguser)

// 2.4登录
// 2.4.1添加语法规则,可以直接用注册的
router.post('/login',expressJOI(schema_user_info), routerHandler.getLogin)

module.exports = router

3.

处理函数模块

// 1.4.1初始化路由处理函数模块

//注册
const db = require('../mysql')
//导入密码加密解密包
const bcrypt = require('bcryptjs')
function getReguser(req, res) {
    // res.send('这里是注册模块')
    // 2.2.4检测用户名是否被占用 导入mysql
    let selectUser = 'select * from users where username = ?'
    db.query(selectUser, req.body.username, (err, results) => {
        // 2.3.1用到我们前面定义的全局中间件优化res.send
        if (err) return res.cc(err)
        if (results.length == 1) return res.cc('用户名已被占用')
        // 2.2.5如果过了前两关的验证 基本可以验证成功了 就先对密码进行加密处理安装bcryptjs
        req.body.password = bcrypt.hashSync(req.body.password, 10)
        // console.log(req.body.password);
        // 2.2.6插入新用户
        let insertUser = 'insert into users set ?'
        // -------------注意一下这里插入数据库的参数怎么写的
        db.query(insertUser, [{username : req.body.username, password : req.body.password}], (err, results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('注册失败')
            res.cc('注册成功',0)
        })
    })
}

//登录
// 2.4.4.1导入jwt
const jwt = require('jsonwebtoken')
const secretKey = require('../secretKey')
const { result } = require('@hapi/joi/lib/base')
function getLogin(req, res) {
    // res.send('这里是登录模块')
    // 2.4.2根据用户名查询用户的数据
    let selectLoginuser = 'select * from users where username = ?'
    db.query(selectLoginuser, req.body.username, (err, results) => {
        if (err) res.cc(err)
        if (results.length !== 1) res.cc('未找到该用户')
        // 2.4.3有数据就去判断密码是否正确
        // console.log(results);
        if (!bcrypt.compareSync(req.body.password, results[0].password)) return res.cc('密码错误')
        // 2.4.4用户名有,密码 也对上了说明登陆成功,开始jwt身份认证
        // 先剔除密码和头像的值
        let user = {...results[0], password : '', user_pic: '',algorithms : ['HS256']}
        let userToken = jwt.sign(user, secretKey.secretKey, {expiresIn : '1h'})
        // 2.4.5向客户端发送token
        res.send({
            status : 0, 
            msg : '登录成功',
            token : 'Bearer ' + userToken
        })
    })
}

// 获取用户基本信息
function getInfo(req,res) {
    // res.send('个人中心')
    // 3.1.2获取用户基本信息
    let selectInfo = 'select id,username,nickname,email,user_pic from users where id = ?'
    db.query(selectInfo, req.user.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1) return res.cc('获取信息失败')
        res.send({
            status : 0,
            msg : '获取信息成功',
            data : results[0]
        })
    })
}

// 3.2更新用户信息
function updateInfo(req, res) {
    // res.send('更新用户信息')
    // 3.2.4更新用户功能
    let updateInfo = 'update users set ? where id = ?'
    db.query(updateInfo, [req.body, req.user.id], (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('更新信息失败')
        res.cc('更新信息成功', 0)
    })
}

// 3.3重置密码
function updatePwd(req, res) {
    // res.send('重置密码')
    // 3.3.2查询用户是否存在
    let selectExist = 'select * from users where id = ?'
    db.query(selectExist, req.user.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1) return res.cc('用户不存在')
        // 3.3.3前面那一步虽然无所谓但这一步必须的 判断输入的旧密码是否正确
        if(!bcrypt.compareSync(req.body.oldPwd, results[0].password)) return res.cc('输入密码错误')
        // 3.3.4对新密码加密后更新到数据库
        let password = bcrypt.hashSync(req.body.newPwd)
        let updatePwd = 'update users set password =? where id =?'
        db.query(updatePwd, [password,req.user.id], (err, results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('修改密码失败')
            res.cc('更新密码成功', 0)
        })
    })
}


// 3.4更换头像
function updateAvatar(req, res) {
    // res.send('更换头像')
    // 3.4.3
    let updateAvatar = 'update users set user_pic = ? where id = ?'
    db.query(updateAvatar, [req.body.avatar, req.user.id], (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('更新头像失败')
        res.cc('更新头像成功', 0)
    })
}
module.exports = {
    getLogin,
    getReguser,
    getInfo,
    updateInfo,
    updatePwd,
    updateAvatar
}

4.

这个时候就可以去api入口文件测试一下了,然后在数据库写好我们的数据表,创立一个js文件链接数据库

// 2.登录注册
// 2.1建好数据库后配置数据库
const { result } = require('@hapi/joi/lib/base')
const mysql = require('mysql')
const db = mysql.createPool({
    host : '127.0.0.1',
    user : 'root',
    password : 'admin123',
    database : 'mydb'
})

// 测试
/* db.query('select 1' , (err, results) => {
    if(err) return console.log(err.message);
    return console.log(results);
}) */

module.exports = db

5.

没记错的话,在登录接口应该使用jwt认证机制生成token吧

const express = require('express')
const router = express.Router()
const routerHandler = require('../router_handler/user')
// 3.个人中心
// 3.1获取用户基本信息
router.get('/userinfo', routerHandler.getInfo)


// 3.2.1更新用户信息
// 3.2.3添加验证规则
const expressJoi = require('@escook/express-joi')
const {schema_update_info, schema_update_avatar} = require('../schema/user')
router.post('/userinfo',expressJoi(schema_update_info),routerHandler.updateInfo)

// 3.3.1重置密码
const {schema_update_pwd} = require('../schema/user')
router.post('/updatepwd',expressJoi(schema_update_pwd), routerHandler.updatePwd)

// 3.4.1更换头像
const {schema_updatee_avatar} = require('../schema/user')
router.post('/update/avatar',expressJoi(schema_updatee_avatar), routerHandler.updateAvatar)

module.exports = router

token的验证规则

module.exports = {
    secretKey : 'sdfafsfds'
}

上面就是整个user部分的接口了包括登录注册,添加删除修改账号或者密码等,下面是我们的user的验证规则

// 2.2注册
// 2.2.1对表单数据验证,这里就不if else了直接用上joi来验证 joi要下最新版而且直接导入joi
const { number } = require('joi')
const joi = require('joi')

const username = joi.string().alphanum().min(1).max(10).required()
// 错误点:正则{}里面的量词之间不能以空格隔开
const password = joi.string().pattern(/^[\S]{6,12}$/).required()

// 3.2.2更新用户信息规则
const id = joi.number().integer().min(1).required()
const nickname = joi.string().required()
const email = joi.string().email().required()

// 3.4.2更换头像规则
const avatar = joi.string().dataUri().required()
module.exports.schema_user_info = {
    body : {
        username,
        password
    }
}

module.exports.schema_update_info = {
    body : {
        id,
        nickname,
        email
    }
}

module.exports.schema_update_pwd = {
    // 3.3.2重置密码规则
    body : {
        oldPwd : password,
        // --------------错误点这里就算是变量也要添加引号
        newPwd : joi.not(joi.ref('oldPwd')).concat(password)
    }
}

module.exports.schema_updatee_avatar = {
    body : {
        avatar
    }
}

6.

这个部分是对文章的名字和别名的增删改查的操作了,这里面的难点在于要去理解那个怎么来判断是否重名哪里

// 4.1.1文章分类列表函数
const { result } = require('@hapi/joi/lib/base')
const db = require('../mysql')
function getArticleList(req, res) {
    // res.send('文章分类列表')
    // 4.1.3获取文章数据
    let selectArticleList = 'select * from article where is_delete = 0'
    db.query(selectArticleList, (err, results) => {
        if (err) return res.cc(err)
        res.send({
            status : 0,
            msg : '获取文章分类列表成功',
            data : results
        })
    })
}

// 4.2.1新增文章分类
function addCates(req,res) {
    // 4.2.3名字与别名是否重名
    let selectDuplicate = 'select * from article where name = ? or alias = ?'
    db.query(selectDuplicate, [req.body.name, req.body.alias], (err, results) => {
        if(err) return res.cc(err)
        if(results.length == 2) return res.cc('文章名字和别名已被占用')
        if(results.length == 1 && results[0].name == req.body.name && results[0].alias == req.body.alias) return res.cc('文章名字和别名已被占用')
        if(results.length == 1 && results[0].name == req.body.name) return res.cc('文章名字被占用')
        if(results.length == 1 && results[0].alias == req.body.alias) return res.cc('文章别名被占用')
        // 4.2.4实现文章分类新增
        let addArt = 'insert into article set ?'
        db.query(addArt, req.body, (err, results) => {
            if(err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('新增文章失败')
            res.cc('新增文章分类成功', 0)
        })
    })
}

// 4.3.1根据idshanchuwenzhan
function deleteCate(req, res) {
    // 4.3.3实现删除功能
    let deleteId = 'update article set is_delete = 1 where id = ?'
    db.query(deleteId, req.params.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('删除文章失败')
        res.cc('删除文章分类成功', 0)
    })
}

// 4.4.1根据id获取文章分类
function requireArt(req,res) {
    // 4.4.2
    let selectArt = 'select * from article where id = ?'
    db.query(selectArt, req.params.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1 || results[0].is_delete == 1) return res.cc('没有该文章')
        res.send({
            status : 0,
            msg : '获取文章分类数据成功',
            data : results[0]
        })
    })
}

// 4.5.1根据id更新文章
function updateArt(req, res) {
    // 4.5.2查看是否重名
    // -----------------这里需要先将自己这一项排除出来
    let selectIdDuplicate = 'select * from article where id != ? and (name = ? or alias = ?)'
    db.query(selectIdDuplicate, [req.body.id, req.body.name, req.body.alias], (err, results) => {
        if (err) return res.cc(err)
        if (results.length == 2) return res.cc('文章名称和别名已被占用')
        if (results.length == 1 && results[0].name == req.body.name && results[0].alias == req.body.alias) return res.cc('文章名称和别名已被占用')
        if (results.length == 1 && results[0].name == req.body.name) return res.cc('文章名称已被占用')
        if (results.length == 1 && results[0].alias == req.body.alias) return res.cc('别名已被占用')
        let updateIdArt = 'update article set ? where id = ?'
        db.query(updateIdArt, [req.body, req.body.id] , (err,results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('文章更新失败')
            res.cc('更新分类信息成功', 0)
        })
    })
}
module.exports = {
    getArticleList,
    addCates,
    deleteCate,
    requireArt,
    updateArt
}

这是路由模块,上面是路由的处理函数

// 4.文章类别管理
const express = require('express')
const router = express.Router()
const routerhanlder = require('../router_handler/article')
// 4.1获取文章分类列表
router.get('/cates',routerhanlder.getArticleList)

// 4.2新增文章分类
const expressJoi = require('@escook/express-joi')
const {add_article_list, update_id_cate} =require('../schema/article')
router.post('/addcates',expressJoi(add_article_list),routerhanlder.addCates)

// 4.3根据id删除文章
const {delete_id_cate} = require('../schema/article')
router.get('/deletecate/:id',expressJoi(delete_id_cate),routerhanlder.deleteCate)

// 4.4根据id获取文章分类数据
router.get('/cates/:id',expressJoi(delete_id_cate), routerhanlder.requireArt)

// 4.5根据id更新文章分类数据
const {update_id_Art} = require('../schema/article')
router.post('/updatecate',expressJoi(update_id_Art) ,routerhanlder.updateArt)

module.exports = router

然后我们的规则

const joi = require('joi')

// 4.2.2新增文章规则
const name = joi.string().required()
const alias = joi.string().alphanum().required()

// 4.3.2根据id删除文章 注意这个id是动态的而且是get请求,所以不再是body数据
const id = joi.number().integer().min(1).required()
module.exports.add_article_list = {
    body : {
        name,
        alias
    }
}

module.exports.delete_id_cate = {
    params : {
        id
    }
}

module.exports.update_id_Art = {
    body : {
        id : id,
        name : name,
        alias : alias
    }
}

7.

最后是我们的添加文章这个功能,就是往每一个刚才创建好的文章里面,添加新文章,直接看逻辑的实现吧

// 5.1.1发布文章函数
const path = require('path')
const db = require('../mysql')
function addCate(req, res) {
    // 5.1.5因为上传表单无法用joi所以要单独规定
    if (!req.file || req.file.fieldname !== 'cover_img') return res.cc('请上传图片')
    // 5.1.6事先发布文章功能
    const cateObj = {
        ...req.body,
        cover_img : path.join('/uploads', req.file.filename),
        pub_date : new Date(),
        author_id : req.user.id
    }
    let addInsert = 'insert into articles set ?'
    db.query(addInsert,  cateObj, (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('发布文章失败')
        res.cc('发布文章成功', 0)
    })
    
}


module.exports = {
    addCate
}

 

 

 

 

// 1.4初始化路由相关文件夹 不光要给路由分装一个模块 里面的处理函数也要有一个模块
const express = require('express')
const { append } = require('express/lib/response')
const router = express.Router()
// 1.4.2导入路由处理函数
const routerHandler = require('../router_handler/user')

// 注册
// 2.2.2导入joi验证输入进来的是否合法
const expressJOI = require('@escook/express-joi')
const {schema_user_info} = require('../schema/user')
router.post('/reguser',expressJOI(schema_user_info), routerHandler.getReguser)

// 2.4登录
// 2.4.1添加语法规则,可以直接用注册的
router.post('/login',expressJOI(schema_user_info), routerHandler.getLogin)

module.exports = router



有关复习 - node.js(接口案例)的更多相关文章

  1. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  2. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  3. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  4. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

  5. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  6. node.js - 如何在 Travis CI 上的一个项目中运行 Node.js 和 Ruby 测试 - 2

    我有一个包含多个组件的存储库,其中大部分是用JavaScript(Node.js)编写的,一个是用Ruby(RubyonRails)编写的。我想要一个.travis.yml文件来触发一个运行每个组件的所有测试的构建。根据thisTravisCIGoogleGroupthread,目前还没有官方支持。我的目录结构是这样的:.├──构建服务器├──核心├──扩展├──网络应用├──流浪文件├──package.json├──.travis.yml└──生成文件我希望能够运行特定版本的Ruby(2.2.2)和Node.js(0.12.2)。我已经有了一个make目标,所以maketest在每

  7. ruby-on-rails - ruby open ssl api for encrypted key (without nodes option) - 2

    在安装了openssllib的linux机器上,当您执行带有“-nodes”选项的“opensslpkcs12”时,您将获得带有未加密私钥的输出,但如果您跳过–nodes选项,则输出将具有加密的私钥。e.g.opensslpkcs12-intest.pfx-outtest.pem你应该看到像下面这样加密的私钥-----BEGINENCRYPTEDPRIVATEKEY-----MIIFDjBABgkqhkiGG7s=-----ENDENCRYPTEDPRIVATEKEY-----如何使用ruby​​的开放ssl库实现上述目标?这就是我用ruby​​生成私钥的方式:@private_key

  8. node.js - 从未编写过任何自动化测试,我应该如何开始行为驱动开发? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。多年来,我一直在使用多种语言进行编程,并且认为自己总体上相当擅长。但是,我从未编写过任何自动化测试:没有单元测试,没有TDD,没有BDD,什么都没有。我已经尝试开始为我的项目编写适当的测试套件。我可以看到在进行任何更改后能够自动测试项目中所有代码的理论值(value)。我可以看到像RSpec和Mocha这样的测试框架应该如何使设置和运行所述测试变得相当容易

  9. ruby-on-rails - 将 Angular JS 与 Rails 集成 - 2

    我需要一些指导来了解如何将Angular整合到rails中。选择Rails的原因:我喜欢他们偏执的做事方式。还有迁移,gem真的很酷。使用angular的原因:我正在研究和寻找最适合SPA的框架。Backbone似乎太抽象了。我不得不在Angular和Ember之间做出选择。我首先开始阅读Angular,它对我来说很有意义。所以我从来没有去读过关于ember的文章。使用Angular和Rails的原因:我研究并尝试使用小型框架,例如grape、slim(是的,我也使用php)。但我觉得需要坚持项目的长期范围。我个人喜欢用Rails的方式做事。这就是我需要帮助的地方,我在Rails4中有

  10. ruby-on-rails - 如何在 RubyOnRails 中使用 'acts as nested set' 创建一个可排序的接口(interface) - 2

    我一直在为使用acts_as_list的模型实现一些不错的交互界面,这些界面可以对我的mRails应用程序中的列表进行排序。我有一个排序函数,在每次拖放之后使用sortable_elementscript.aculo.us函数调用并设置每条记录的位置。这是在拖放完成后处理排序的Controller操作示例:defsortparams[:documents].each_with_indexdo|id,index|Document.update_all(['position=?',index+1],['id=?',id])endend现在我正在尝试对嵌套集模型(acts_as_nested

随机推荐