大家好,欢迎来到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



