Day2 koa从入门到发疯 | 青训营

241 阅读6分钟

如何发送HTTP响应

拿到GET过来的数据

在 Koa 中,HTTP 请求对象 ctx.request 包含了客户端发送给服务器的所有请求信息,包括请求头、请求体、请求参数等。其中,请求体中包含了客户端发送给服务器的请求数据,例如表单数据、JSON 数据等。我们在获取GET请求过来的参数时一般有两种方式: ctx.request.queryctx.query

const koa = require('koa')
const Router = require('koa-router')

router.get('/del', async (ctx) => {
    //以下两种都可以接受get请求的参数
    const { id } = ctx.request.query
    const { id1 } = ctx.query

    console.log(id, id1, id2);
    // ctx.body = '用户列表页'

    /**
     * ctx.set 方法接受两个参数,
     * 第一个参数是要设置的 HTTP 响应头的键(header name),
     * 第二个参数是要设置的 HTTP 响应头的值(header value)
     * */
    ctx.set("Allow", "GET,POST")//可以设置响应头和请求方式
    ctx.status = 301,//可以设置他的返回状态码
        ctx.body = {//可以通过body响应一个JSON对象
            code: 200,
            msg: '这是请求首页信息'
        }
})

app.use(router.routes())

app.listen(3000)

设置响应头

const koa = require('koa')
const Router = require('koa-router')

router.get('/del', async (ctx) => {
    /**
     * ctx.set 方法接受两个参数,
     * 第一个参数是要设置的 HTTP 响应头的键(header name),
     * 第二个参数是要设置的 HTTP 响应头的值(header value)
     * */
    ctx.set("Allow", "GET,POST")//可以设置响应头和请求方式
    ctx.status = 301,//可以设置他的返回状态码
    ctx.body = {//可以通过body相应一个JSON对象
        code: 200,
        msg: '这是请求首页信息'
    }
})

app.use(router.routes())
app.listen(3000)

如何拿POST过来的数据

POST过来的参数获取和GET有区别,主要区别在于post请求需要利用一个中间件(body-parse),把请求体解析成js对象,并挂载到ctx的.request.body属性上,我们才能对其操作

const koa = require('koa')
const Router = require('koa-router')
//先引入body-parse
const bodyparser = require('koa-bodyparser')

router.post('/add', async (ctx) => {

    //接受post请求的参数
    /*
    body-parser 是 Node.js 中一个常用的请求体解析库,
    它可以将请求体数据解析成 JavaScript 对象,
    并将其挂载到 req.body 上,方便我们在路由处理函数中进行访问和处理。
    */
    const { username, pwd } = ctx.request.body
    console.log(username, pwd);
    ctx.body = '添加用户'
})

app.use(bodyparser())//这里需要注意的是需要先调用bodyparse再use routes,否则数据体无法正确解析
app.use(router.routes())
app.listen(3000)

如何拿路由携带的参数

在 Web 应用程序中,路由参数(Route Parameter)是指嵌入在 URL 路径中的变量,用于标识资源的唯一标识符或其他相关信息。在 Koa 中,路由参数可以通过冒号 : 开头的占位符来定义,例如 /find/:id 中的 :id 就表示一个路由参数。

//这种是直接跟在路径后面的路由参数
router.get('/find/:id', async (ctx) => {
    let id = ctx.params.id
    console.log(id);
})

如何设置错误处理

koa自带的方法

ctx.throw(412, '先决条件失败')ctx.throw是服务端抛出异常的函数

封装一个中间件来处理错误

利用trycatch进行封装

app.use((ctx,err){
    try(){}catch(err){}
})

使用插件

koa-json-error,安装koa-json-error,让服务端可以抛出比较完善的错误信息

{
    "message": "先决条件失败",
    "name": "PreconditionFailedError",
    "stack": "PreconditionFailedError: 先决条件失败\n    at Object.throw (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\koa\\lib\\context.js:97:11)\n    at D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\app.js:31:18\n    at dispatch (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-compose@4.1.0\\node_modules\\koa-compose\\index.js:42:32)\n    at D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-router@12.0.0\\node_modules\\koa-router\\lib\\router.js:425:16\n    at dispatch (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-compose@4.1.0\\node_modules\\koa-compose\\index.js:42:32)\n    at D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-compose@4.1.0\\node_modules\\koa-compose\\index.js:34:12\n    at dispatch (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-router@12.0.0\\node_modules\\koa-router\\lib\\router.js:430:31)\n    at dispatch (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\koa-compose\\index.js:42:32)\n    at jsonError (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\.store\\koa-json-error@3.1.2\\node_modules\\koa-json-error\\lib\\middleware.js:49:12)\n    at dispatch (D:\\MyNecessaryFile\\A技术学习\\koa2-study\\myKoa\\node_modules\\koa-compose\\index.js:42:32)",
    "status": 412
}

如何连接到数据库,以Mango为例

  1. 创建一个db(数据库)文件夹,并创建index.js,引入mangoose,告诉node我正在使用mongodb数据库const mongoose = require('mongoose')
  2. 向外导出模块
    module.exports = () => {
     //connect连接到数据库,他有两个参数,一个是数据库的地址,第二个是相关配置
     /**useNewUrlParser是 Mongoose 连接 MongoDB 数据库时的一个选项,
     用于告诉 Mongoose 使用 MongoDB 连接字符串中的新的解析器(parser)。 */
     mongoose.connect('mongodb://localhost:27017/demo', { useNewUrlParser: true }).then(() => {
         console.log('数据库连接成功');
     }).catch(err => {
         console.log('账号连接失败', err);
     })
     }
    

如何使用MongoDB封装一个增删改查逻辑层

Step1封装公有操作

我们可以封装一个CRUDUtil,用来存放基本的增删改查操作。

/**
 * 这里是一些公共的crud方法
 */
const models = require('../../models')
/**
 * 
 * 用于查询数据的公共方法
 * @param {*} model 模型对象
 * @param {*} ctx   context 
 * @param {*} where 条件
 * @returns 
 */
const find = (model, ctx, where) => {//传入两个模块,查找的目标,属于哪个模块,where:查询对象
    return model.find(where || {}).then(//where:条件查询
        (rel) => {
            console.log(ctx);
            if (rel != null && rel.length > 0) {
                ctx.body = {
                    result: JSON.parse(JSON.stringify(rel))
                }
            } else {
                ctx.body = {
                    code: 404,
                    msg: '未找到指定数据'
                }
            }
        }).catch(err => {
            ctx.body = {
                code: 400,
                msg: '查询时出错'
            }
            console.error(err);
        })
}
/**
 * 添加数据的公共方法
 * @param {*} model 
 * @param {*} params 
 * @param {*} ctx 
 * @returns 
 */
const add = (model, params, ctx) => {

    // create()用于向MongoDB中的Users集合中插入一条新的文档
    return model.create(params).then((rel) => {  //调用这个模板的create方法
        if (rel) {
            ctx.body = {
                code: 200,
                msg: '添加成功',
                rel
            }
        } else {
            ctx.body = {
                code: 300,
                msg: '添加失败'
            }
        }
    }).catch((err) => {
        ctx.body = {
            code: 400,
            msg: '添加出现异常'
        }
        console.error(err);
    })
}
const update = (model, where, params, ctx) => {
    return model.updateOne(where, params).then(rel => {
        if (rel != null) {
            ctx.body = {
                code: 200,
                msg: '修改成功'
            }
        }
    }).catch(err => {
        ctx.body = {
            code: 400,
            msg: '修改时出现异常'
        }
        console.error(err);
    })
}
/**
 * 用于数据删除的公共方法
 * @param {*} model 
 * @param {*} where 
 * @param {*} ctx 
 */
const del = (model, where, ctx) => {
    model.findByIdAndDelete(where).then(rel => {

        ctx.body = {
            result: rel

        }
    }).catch(err => {
        ctx.body = {
            code: 400,
            msg: '删除异常'
        }
        console.error(err)
    })
}
/**
 * 用于查询单个数据的公共方法
 * @param {*} model 
 * @param {*} where 
 * @param {*} ctx 
 */
const findOne = (model, where, ctx) => {
    return model.findOne(where || {}).then(rel => {
        if (rel != null) {

            ctx.body = {
                result: JSON.parse(JSON.stringify(rel))
            }

        } else {

            ctx.body = {
                code: 404,
                msg: '未找到指定数据'
            }

        }
    }).catch(err => {

        ctx.body = {
            code: 400,
            msg: '查询时出错'
        }

        console.error(err)
    })
}
module.exports = {
    find,
    add,
    update,
    del,
    findOne
}

以上代码暂未对模糊查询等find方法进行封装,目前仅实现了查询单条数据的功能。

Step2在具体逻辑实现层调用封装好的crud方法

在实现逻辑的userController调用先前封装好的方法,以减少冗余的代码

const { Users } = require('../models');
const crud = require('./CRUDUtil/index');

const userAdd = async (ctx) => {
    let params = ctx.request.body || null;
    await crud.add(Users, params, ctx);
};

const userDel = async (ctx) => {
    let { _id } = ctx.request.body || null;
    let result = await crud.del(Users, { _id }, ctx);
    ctx.body = result;
};

const userUpdate = async (ctx) => {
    let params = ctx.request.body || null;
    await crud.update(Users,
        { _id: params._id },
        {
            userAccount: params.userAccount,
            userPassword: params.userPassword,
            userAvatar: params.userAvatar,
            userName: params.userName,
            updateTime: new Date()
        },
        ctx
    );
};

const userFindAll = async (ctx) => {
    await crud.find(Users, (ctx ? ctx : {}), null);
};

const userFind = async (ctx) => {
    await crud.findOne(Users, { _id: ctx.params.id }, ctx || null);
};

module.exports = {
    userAdd,
    userDel,
    userUpdate,
    userFindAll,
    userFind
};

总结

这样处理我们的代码一是能让我们的代码看起来更整洁,也将逻辑层与具体的基本操作函数分离,更符合resful,代码维护性更强。