koa2学习笔记

koa2学习笔记Koa2 学习笔记 koa2

大家好,欢迎来到IT知识分享网。


介绍

Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。


一、Koa2安装

创建一个空白目录,然后进入终端,并在终端对koa进行安装:

# 项目初始化 cnpm init -y # 安装koa2 cnpm i koa2 -S 

二、入口文件

在项目根目录创建 app.js 文件,并在上一步操作中生成的 package.json 里配置:

{ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node app.js" }, } 

在 app.js 中:

const Koa = require('koa2'); const app = new Koa() const port = 5050; // const router = require('./router/index') // const cors = require('koa2-cors') // const static = require('koa-static') // const path = require('path') // 统一异常处理 const errorHandler = require('./utils/errorHandler') /* app.use()方法是:将给定的中间件方法添加到此应用程序。简单说就是调用中间件 app.use() 返回 this, 因此可以链式表达 */ // app.use(static(path.join(__dirname+'/assets'))) // 读取静态文件 // app.use(cors()); // cors中间件一定要写在路由之前(后端允许跨域) // app.use(router.routes(), router.allowedMethods()); //引入路由 errorHandler(app) app.use(async (ctx)=>{ ctx.body = "Hello, Koa"; // ctx.body是ctx.response.body的简写 }) app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`) }) 

然后运行 npm start ,并在浏览器输入 http://localhost:5050/ 即可看到页面效果。

三、洋葱模型

在这里插入图片描述
Koa 和 Express 都会使用到中间件,Express的中间件是顺序执行,从第一个中间件执行到最后一个中间件,发出响应:
在这里插入图片描述

// 整个koa项目的入口文件 // 构造函数 const Koa = require('koa2'); // 声明一个实例 const app = new Koa() // 端口号 const port = 5050; // 调用中间件 app.use(async (ctx, next) => { // 返回数据给页面 // ctx.response.body = '这是koa首页'; console.log(1) await next() console.log(1) }) app.use(async (ctx, next) => { console.log(2) await next() console.log(2) }) app.use(async (ctx, next) => { console.log(3) }) // baidu.com ip+port app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`) }) 

那么在浏览器刷新后,控制台得到的顺序是

1 2 3 2 1 

现在可以看到,我们通过 next可以先运行下个中间件,等中间件结束后,再继续运行当前 next() 之后的代码。

四、路由安装

当需要匹配不同路由时,可以安装

npm i koa-router 

将app.js修改为

const Koa = require('koa2'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); const port = 5050; router.get('/', async (ctx)=>{ ctx.body = "首页"; }) router.get('/list', async (ctx)=>{ ctx.body = "列表页"; }) app.use(router.routes(), router.allowedMethods()); app.listen(port, ()=>{ console.log('Server is running at http://localhost:'+port); }) 

此时,到浏览器刷新并在地址栏最后添加 /list 即可得到首页和列表页。

五、路由拆分

1、创建 router 文件夹

# app.js const router = require('./router/index') app.use(router.routes(), router.allowedMethods()); # index.js // 引入路由 const Router = require('koa-router'); // const list = require('./list'); const router = new Router(); const home = require('./home') const list = require('./list') const login = require('./login') router.get('/home', async (ctx) => { ctx.body = '首页'; }) router.get('/list', async (ctx) => { ctx.body = '列表页' }) router.use('/list', list.routes(), list.allowedMethods()) router.use('/home', home.routes(), home.allowedMethods()) router.use('/login', login.routes(), login.allowedMethods()) router.redirect('/', '/home') // 打开网页自动重定向到首页 module.exports = router # home.js // 所有api接口都在这个文件 const Router = require('koa-router'); const home = new Router(); // const db = require('../utils/db') home.get('/fruit', async (ctx) => { ctx.body = '首页-水果'; }) home.get('/wanju', async (ctx) => { // 访问数据库,拿到数据返回给前端 /* let mydata = await new Promise((resolve, reject) => { let sql = 'select * from article'; db.query(sql, (err, data) => { if (err) throw err; // 对数据进行处理 eg: data.map(val => { val.imgUrl = 'http://localhost:5050'+val.imgUrl }) resolve(data) }) }) ctx.body = mydata; */ ctx.body = "首页-玩具"; }) home.get('/film', async (ctx) => { ctx.body = '首页-电影票'; }) module.exports = home # list.js // 所有api接口都在这个文件 const Router = require('koa-router'); const list = new Router(); list.get('/fruit', async (ctx) => { ctx.body = '列表页-水果'; }) list.get('/wanju', async (ctx) => { ctx.body = '列表页-玩具'; }) list.get('/film', async (ctx) => { ctx.body = '列表页-电影票'; }) module.exports = list #登录页login.js (要用到自己的数据库,需要额外创建db.js文件,后面说) // 所有api接口都在这个文件 const Router = require('koa-router'); const login = new Router(); const bodyparser = require('koa-bodyparser') const db = require('../utils/db') const jwt = require('jsonwebtoken') login.use(bodyparser()) // 调用这个中间件之后,就可以拿到前端传过来的数据。 login.post('/register', async (ctx) => { console.log(ctx.request.body) let account = ctx.request.body.account; // 前端过来的账号 let pwd = ctx.request.body.pwd; // 判断数据库里是否有这个账号,如果有就验证密码,如果没有就增加一条记录 let sql = `select * from users where account = '${account}'` let myarr = await new Promise((resolve, reject) => { return db.query(sql, (err, data) => { if (err) throw err; console.log(data) resolve(data) }) }) if (myarr.length > 0) { // 证明有这个账号,验证密码 console.log(myarr) if (myarr[0].pwd == pwd) { ctx.body = { code: 200, msg: "登录成功", token: myarr[0].token, account: myarr[0].account } } else { ctx.body = { code: 300, msg: "账号或密码错误" } } } else { // 没有账号,要注册(增加一条数据) let token = jwt.sign({ account: account, pwd: pwd }, 'secret', {expiresIn:3600}) let insertSql = `insert into users(account, pwd, token) values ('${account}', '${pwd}', '${token}')` ctx.data = await new Promise((resolve, reject) => { return db.query(insertSql, (err, data) => { if (err) throw err; console.log(data) ctx.body = { code: 200, msg: "注册成功", token: token, account: account } resolve(ctx.data) }) }) } // ctx.body = `登录注册`; }) module.exports = login #errorPage.js const Router = require('koa-router'); const errorPage = new Router(); errorPage.get('/', async (ctx) => { ctx.body = "访问页面不存在" }) module.exports = errorPage 

到浏览器刷新 localhost:5050/home 与 localhost:5050/list 即可得到首页与列表页。

2、路由重定向

router.use('/home', home.routes(), home.allowedMethods()); ... router.redirect('/', '/home'); 

3、404无效路由

如果被访问到无效路由,可以统一返回404页面:

在 router 下 errorPage.js :

const Router = require('koa-router'); const errorPage = new Router(); errorPage.get('/', async (ctx) => { ctx.body = "访问页面不存在" }) module.exports = errorPage 

在 app.js 中引用:

// 匹配不到页面的全部跳转去404 app.use(async (ctx, next) => { await next(); if (parseInt(ctx.status) === 404) { ctx.response.redirect("/404") } }) app.use(router.routes(), router.allowedMethods()); 

六、统一异常处理

module.exports = (app) => { app.use(async(ctx, next) => { let status = 200; let data = ""; try { await next(); status = ctx.status; } catch (err) { status = 500; } if (status >= 400) { switch (status) { case 400: case 404: case 500: data = status; break; default: data = 'other'; break; } } ctx.response.status = status; console.log(data) }) } 

然后在 app.js 中引入:

const errorHandler = require('./utils/errorHandler.js'); app.use(router.routes(), router.allowedMethods()); ... errorHandler(app); 

七、操作mysql函数封装

首先,项目内安装 mysql:

yarn add mysql // 或者 cnpm i mysql 

在 utils 目录下创建一个 db.js 文件:

let mysql = require('mysql') let pool = mysql.createPool({ host: 'localhost', // 连接的服务器(代码托管到线上后,需改为内网IP,而非外网) port: 3306, // mysql服务运行的端口 database: 'c_data', // 选择的库 user: 'root', // 用户名 password: 'root' // 用户密码 }) // 对数据库进行增删改查的基础 function query(sql, callback) { pool.getConnection(function (err, connection) { connection.query(sql, function (err, rows) { callback(err, rows) connection.release() // 中断连接 }) }) } exports.query = query 

调用方式:

const db = require('../utils/db.js'); home.get('/wanju', async (ctx) => { let mydata = await new Promise((resolve, reject) => { // 访问数据库,拿到数据返回给前端 let sql = 'select * from article'; db.query(sql, (err, data) => { if (err) throw err; // 对数据进行处理 eg: data.map(val => { val.imgUrl = 'http://localhost:5050'+val.imgUrl }) resolve(data) }) }) ctx.body = mydata; }) 

八、后端允许跨域

前端想跨域,可以设置proxy。如果后端允许跨域,可以如下操作:

// 安装koa2-cors cnpm i koa2-cors // 这里cors中间件一定要写在路由之前 app.js app.use(cors()); app.use(router.routes(), router.allowedMethods()) 

九、读取静态资源文件

首先,在项目的根目录下创建 assets 后,将图片资源文件夹 images 放到其中,并且执行以下操作:

// 安装koa-static cnpm install koa-static // 引入 const path = require('path') const static = require('koa-static') // 获取静态资源文件夹 app.use(static(path.join(__dirname+'/assets'))); ... app.use(router.routes(), router.allowedMethods()) 

在这里插入图片描述
在这里插入图片描述
假设其中有一txt文件叫做 1.txt,那么我们打开浏览器,访问:http://localhost:5050/1.txt 即可得到。这里

十、POST请求

1、建表

设定字段为account和pwd

create table users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, account VARCHAR(20) NOT NULL COMMENT '账号', pwd VARCHAR(20) NOT NULL COMMENT '密码', token LONGTEXT NOT NULL COMMENT '令牌' ); 

2、form表单页面

在 assets 下创建 index.html :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <label for="account">账号</label>
    <input type="text" value="" name="account" class="account" placeholder="请输入账号" />
    <br><br>
    <label for="pwd">密码</label>
    <input type="password" value="" name="pwd" class="pwd" placeholder="请输入密码" />
    <br><br>
    <button class="btn">登录/注册</button>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    $('.btn').click(()=>{
        $.ajax({
            url: "/login/register",
            method: "POST",
            data: {
                account: $('.account').val(),
                pwd: $('.pwd').val()
            },
            success(res){
                console.log(res)
            },
            error(err){
                console.log(err)
            }
        })
    })
</script>

在浏览器直接访问 http://localhost:5050/index.html 即可进入表单页。

3、安装中间件

安装 koa-bodyparser 与 jsonwebtoken 中间件:

// koa-bodyparser用于获取post请求数据 cnpm install koa-bodyparser --save // jsonwebtoken用于生成token cnpm install jsonwebtoken --save 
JWT

在用户登录的路由中使用 jwt.sign 来生成token,一共定义三个参数,第一个是存入token的信息,第二个是token的钥匙,和config/passport.js的配置的钥匙相同,第三个是保存的时间,3600即一个小时,最后返回token,要在前面加Bearer:

const jwt = require('jsonwebtoken') const token = jwt.sign({ myaccount: myaccount, mypwd: mypwd }, 'secret', { expiresIn: 3600 }) let obj = { token, msg: '登录成功' } resolve(obj) 

4、添加post接口

在 router/login.js 中加入:

const bodyParser = require('koa-bodyparser') login.use(bodyParser()); login.post('/register', async (ctx)=>{ console.log(ctx.request.body); // 可以打印得到数据 ctx.response.body = "登录或注册" }) 

5、登录与自动注册

// 所有api接口都在这个文件 const Router = require('koa-router'); const login = new Router(); const bodyparser = require('koa-bodyparser') const db = require('../utils/db') const jwt = require('jsonwebtoken') login.use(bodyparser()) // 调用这个中间件之后,就可以拿到前端传过来的数据。 login.post('/register', async (ctx) => { console.log(ctx.request.body) let account = ctx.request.body.account; // 前端过来的账号 let pwd = ctx.request.body.pwd; // 判断数据库里是否有这个账号,如果有就验证密码,如果没有就增加一条记录 let sql = `select * from users where account = '${account}'` let myarr = await new Promise((resolve, reject) => { return db.query(sql, (err, data) => { if (err) throw err; console.log(data) resolve(data) }) }) if (myarr.length > 0) { // 证明有这个账号,验证密码 console.log(myarr) if (myarr[0].pwd == pwd) { ctx.body = { code: 200, msg: "登录成功", token: myarr[0].token, account: myarr[0].account } } else { ctx.body = { code: 300, msg: "账号或密码错误" } } } else { // 没有账号,要注册(增加一条数据) let token = jwt.sign({ account: account, pwd: pwd }, 'secret', {expiresIn:3600}) let insertSql = `insert into users(account, pwd, token) values ('${account}', '${pwd}', '${token}')` ctx.data = await new Promise((resolve, reject) => { return db.query(insertSql, (err, data) => { if (err) throw err; console.log(data) ctx.body = { code: 200, msg: "注册成功", token: token, account: account } resolve(ctx.data) }) }) } // ctx.body = `登录注册`; }) module.exports = login 

此时,前端做这个post请求后,就会得到相应的数据。

十一、文件读写

在utils目录下创建file.js

const fs = require('fs') const path = require('path') const db = require('./db') // 读取assets目录下的1.txt和2.txt函数,传入参数文件名 function readFileFn(arg) { return new Promise((resolve, reject) => { let mypath = path.join(__dirname, `../assets/${arg}.txt`) fs.readFile(mypath, (err, data) => { if (err) throw err; resolve(data.toString()) console.log(data.toString()) }) }) } // function readFileFn(arg) { // let mypath = path.join(__dirname, `../assets/${arg}.txt`) // fs.readFile(mypath, (err, data) => { // if (err) throw err; // console.log(data.toString()) // 这里读取的文件是二进制文件流,因此要转字符串 // }) // } // let text1 = readFileFn('1') // let text2 = readFileFn('2') // 把读取到的内容插入特定的结构体中,插入数据库 let fn = async () => { let text1 = await readFileFn('1') let text2 = await readFileFn('2') let arr = [ { id: 0, title: "一套框架多种平台 移动端&桌面端", author: "张三丰", date: "2022-05-21", imgUrl: "/images/dt.png", content: text1 }, { id: 1, title: "渐进式的JavaScript框架", author: "小鱼儿", date: "2022-05-21", imgUrl: "/images/dt.png", content: text2 } ] arr.map(val => { let sql = `insert into article values(${val.id}, '${val.title}', '${val.author}', '${val.date}', '${val.imgUrl}', '${val.content}')` db.query(sql, (err, data) => { if (err) throw err; console.log(data) }) }) } fn() // const data = [ // {id: 0, icon: './images/angluar.gif', subtitle: "学会用Angular构建应用,把这些代码和能力复用在多种不同平台的应用上", title: "一套框架多种平台 移动端&桌面端"}, // { id: 1, icon: './images/vue.gif', subtitle: "不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩", title: "渐进式的JavaScript框架" } // ] // data.map(val => { // let sql = `insert into zixun values (${val.id}, '${val.title}', '${val.subtitle}', '${val.icon}')` // db.query(sql, (err, data) => { // if (err) console.log(err) // console.log(data) // }) // }) 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/114160.html

(0)
上一篇 2025-12-10 14:15
下一篇 2025-12-10 14:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信