koa是由Express原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web框架。使用koa编写web应用,通过组合不同的generator,可以免除重复繁琐的 回调函数嵌套 ,并极大地提升错误处理的效率。koa不在内核方法中绑定任何中间件,它仅仅提供了-个轻量优雅的函数库,使得编写Web应用变得得心应手。
koa和express的区别
-
Koa采用洋葱模型
通常都会说Koa是洋葱模型,这重点在于中间件的设计。但是按照上面的分析,会发现Express也是类似的,不同的是Express中间件机制使用了Callback 实现,这样如果出现异步则可能会使你在执行顺序上感到困惑,因此如果我们想做接口耗时统计、错误处理Koa的这种中间件模式处理起来更方便些。最后一点响应机制也很重要,
Koa不是立即响应,是整个中间件处理完成在最外层进行了响应,而Express则是立即响应。 -
Koa更轻量
koa不提供内置的中间件;
koa不提供路由,而是把路由这个库分离出来了(koa/router) -
Context对象
koa增加了一个Context的对象,作为这次请求的上下文对象(在koa2中作为中间件的第一个参数传入)。同时Context上也挂载了Request和Response两个对象。Express类似, 这两个对象都提供了大量的便捷方法辅助开发这样的话对于在保存一些公有的参 数的话变得更加合情合理。 -
异步流程控制
express采用callback来处理异步,koa采用async/await。
async/await使用同步的写法来处理异步,明显好于callback和promise, -
中间件模型
express基于connect中间件,线性模型;
koa中间件采用洋葱模型(对于每个中间件,在完成了-些事情后,可以非常优雅的将控制权传递给下一个中间件,并能够等待它完成,当后续的中间件完成处理后,控制权又回到了自己)
代码示例:
同步代码
同步方法没有什么区别:
- 01-express-同步.js
const express = require("express")
const app = express()
app.use((req, res, next) => {
console.log("111111")
next()
console.log("333333")
res.send("hello world")
})
app.use((req, res, next) => {
// 同步操作
console.log("22222")
})
app.listen(3000)
运行输出
111111
22222
333333
- 01-koa-同步 .js
const Koa = require("koa")
const app = new Koa()
app.use((ctx, next) => {
console.log("111111")
next()
console.log("333333")
ctx.body("hello world")
})
app.use((ctx, next) => {
// 同步操作
console.log("22222")
})
app.listen(3000)
运行输出:
111111
22222
333333
异步代码
next()表示可以执行下一个中间件,当下一个中间件执行完成之后,如果上一个中间件没有执行完,再返回上一个中间件继续执行。
01-express-异步.js
const express = require("express")
const app = express()
app.use(async (req, res, next) => {
console.log("111111")
await next()
console.log("444444")
res.send("hello world")
})
app.use(async (req, res, next) => {
console.log("22222")
// 异步操作
await delay(1000)
console.log("33333")
})
function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(resolve,time)
})
}
app.listen(3000)
运行输出:
111111
22222
444444
33333
由于next()返回的不是promise对象因此await不起作用,所以输出不会像我们所想输出
- 01-koa-异步.js
const Koa = require("koa")
const app = new Koa()
app.use((ctx, next) => {
console.log("111111")
next()
console.log("444444")
})
app.use((ctx, next) => {
console.log("22222")
// 异步操作
delay(1000)
console.log("33333")
})
function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(resolve,time)
})
}
app.listen(3000)
运行输出:
111111
22222
33333
444444
koa洋葱模型,正常执行。
koa下载安装
npm init
npm i koa
- Koa基本框架
const Koa = require("koa")
const app = new Koa()
// ctx=context 相当于res和req的合并
app.use((ctx, next) => {
})
app.listen(3000)
koa的基本使用
- ctx
ctx.req//node原生的request对象
ctx.res//node原生的response对象
cts.request//koa封装好的的request对象
cts.response//koa封装好的的response对象 - 向返回数据到前端页面
ctx.response.body = 返回的内容
// 返回代码片段
ctx.response.body = "<b>hello world</b>"
// 返回json数据
ctx.response.body = { name: "yang", age: 18 }
但是后面返回的会覆盖前面返回的
- 获取请求路径
ctx.request.path
console.log(ctx.request.path)
-
注: ctx.request.path可以简写成 ctx.path
ctx的多数方法都可以省去request 和 response。
koa的路由
koa中没有路由,需要下载另一个模块
- 下载:
npm i koa-router
- 使用:
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
router.get("/list",(ctx, next) => {
ctx.body = ["111","222","333"]
})
// 注册路由
app.use(router.routes())
// 当请求方法错误时(比如需要get请求发成了post请求),提示相应错误
app.use(router.allowedMethods())
app.listen(3000)
- 路由实现增删改查
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
router.post("/list",(ctx, next) => {
ctx.body = {
ok: 1,
info:"add list success"
}
})
router.get("/list",(ctx, next) => {
ctx.body = ["111","222","333"]
})
router.put("/list/:id", (ctx, next) => {
console.log(ctx.params)
ctx.body = {
ok: 1,
info:"put list success"
}
})
router.del("/list/:id", (ctx, next) => {
ctx.body = {
ok: 1,
info:"del list success"
}
})
// 注册路由
app.use(router.routes())
// 当请求方法错误时(比如需要get请求发成了post请求),提示相应错误
app.use(router.allowedMethods())
app.listen(3000)
- 路由文件拆分,紫萼在一根文件中太乱了,不同的请求应该放在不同的路由文件中,然后进行引入。
index.js:
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
const userRouter = require("./routes/user")
const listRouter = require("./routes/list")
// 先注册路由级组件
router.use("/user", userRouter.routes(),userRouter.allowedMethods())
router.use("/list",listRouter.routes(),listRouter.allowedMethods())
// 再注册应用级组件
app.use(router.routes())
// 当请求方法错误时(比如需要get请求发成了post请求),提示相应错误
app.use(router.allowedMethods())
app.listen(3000)
user.js
const Router = require("koa-router")
const router = new Router()
router.post("/",(ctx, next) => {
ctx.body = {
ok: 1,
info:"add user success"
}
})
router.get("/",(ctx, next) => {
ctx.body = ["111","222","333"]
})
router.put("/:id", (ctx, next) => {
console.log(ctx.params)
ctx.body = {
ok: 1,
info:"put user success"
}
})
router.del("/:id", (ctx, next) => {
ctx.body = {
ok: 1,
info:"del user success"
}
})
module.exports = router
list.js
const Router = require("koa-router")
const router = new Router()
router.post("/",(ctx, next) => {
ctx.body = {
ok: 1,
info:"add list success"
}
})
router.get("/",(ctx, next) => {
ctx.body = ["111","222","333"]
})
router.put("/:id", (ctx, next) => {
console.log(ctx.params)
ctx.body = {
ok: 1,
info:"put list success"
}
})
router.del("/:id", (ctx, next) => {
ctx.body = {
ok: 1,
info:"del list success"
}
})
module.exports = router
- 也可以将 index.js中的
注册路由级组件
提出来
user.js和list.js不用修改,再router下新建index.js
index.js
const Koa = require("koa")
const app = new Koa()
const router = require("./routes")
// 再注册应用级中间件
app.use(router.routes())
// 当请求方法错误时(比如需要get请求发成了post请求),提示相应错误
app.use(router.allowedMethods())
app.listen(3000)
router/index.js:
const Router = require("koa-router")
const router = new Router()
const userRouter = require("./user")
const listRouter = require("./list")
// 统一加前缀
router.prefix("/api")
// 先注册路由级中间件
router.use("/user", userRouter.routes(),userRouter.allowedMethods())
router.use("/list", listRouter.routes(), listRouter.allowedMethods())
module.exports = router
- router统一加前缀
// 统一加前缀
router.prefix("/api")
- 路由的重定向
router.use("/home", homeRouter.routes(), homeRouter.allowedMethods())
router.redirect("/","/home")//重定向
表示当访问更目录时会自动成定向到/home.
koa静态资源
- 安装模块:
npm i koa-static
- 引入模块,配置静态资源:
index.js:
const Koa = require("koa")
const app = new Koa()
const static = require("koa-static")
const path= require("path")
// 先注册路由级中间件
const router = require("./routes")
// 再注册应用级中间件
app.use(router.routes())
app.use(static(path.join(__dirname,"public")))
// 当请求方法错误时(比如需要get请求发成了post请求),提示相应错误
app.use(router.allowedMethods())
app.listen(3000)
- 例子:
center.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- href="css/center.css" 路径前面不要加前缀路径,因为在index.js中配置好了,静态资源会自动去public文件夹下寻找-->
<link rel="stylesheet" href="/css/center.css"/>
</head>
<body>
<div>
center
</div>
</body>
</html>
center.css
div{
background-color: brown;
}
运行访问:http://localhost:3000/center.html
koa获取请求参数
get请求参数获取
在koa中,获取GET请求数据源头是koa中request对象中的query
方法或querystring
方法。
query返回是 格式化好的参数对象,
querystring返回的是请求字符串,
由于ctx对request的API有 直接引用的方式,所以获取GET请求数据有两个途径:
- 从上下文中直接获取:
请求对象ctx.query
,返回如{ a:1, b:2 }
请求字符串ctx.querystring
,返回如a=1&b=2 - 是从上下文的request对象中获取:
请求对象ctx.request.query
,返回如{ a:1, b:2 }
请求字符串ctx.request.querystring
,返回如a=1&b=2
post请求参数获取
对于POST请求的处理,koa-bodyparser
中间件可以把koa上下文的formData数据解析到ctx.request.body
中
- 配置注册模块
const bodyParser = require('koa-bodyparser')
//使用ctx.body解析中间件
app.use(bodyParser())
- 获取
ctx.request.body
(不能简写)
输出:eg:
{ username: 'bbb', password: '123456' }
koa的ejs模板
- 需要安装两个模块
npm install --save koa-views
npm install --save ejs
- 模板配置
const views = require("koa-views")
// 配置模板引擎,将ejs模板文件放在views文件夹下
app.use(views(path.join(__dirname,"views"),{extension:"ejs"}))
- 模板使用
/home请求
const Router = require("koa-router")
const router = new Router()
router.get("/", async (ctx, next) => {
// 是异步的,需要等待模板解析完成之后再返回页面
await ctx.render("home",{username:"yang"})// 自动去views下找home.ejs(入口index.js中配置了)
})
module.exports = router
home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Home</h1>
<h2>欢迎<%=username%>回来</h2>
</body>
</html>
输出:
koa- 登录鉴权
cookie和session
koa-cookie
koa提供了从上下文直接读取、写入cookie的方法
-
ctx.cookies.get(name, [options])
读取上下文请求(前端)中的cookie -
ctx.cookies.set(name, value, [options])
在上下文中写入cookie
koa-session
- 需要下载模块
koa-session-minimal
npm i koa-session-minimal
- 模块配置
index.js:
拦截
const Koa = require("koa")
const app = new Koa()
const static = require("koa-static")
const path = require("path")
const bodyParser = require('koa-bodyparser')
const views = require("koa-views")
const session = require("koa-session-minimal")
// 先注册路由级中间件
const router = require("./routes")
//使用ctx.body解析中间件,获取post参数
app.use(bodyParser())
// 配置静态资源
app.use(static(path.join(__dirname,"public")))
// 配置模板引擎
app.use(views(path.join(__dirname, "views"), { extension: "ejs" }))
// session配置
app.use(session({
// 设置钥匙
key: "yangsessionId",
cookie:{
maxAge:1000*60
}
}))
// session判断拦截
app.use(async (ctx, next) => {
if (ctx.url.includes("login")) {
await next()
return
}
if (ctx.session.user) {
ctx.session.date = Date.now()
await next()
}else{
ctx.redirect("/login")
}
})
// 再注册应用级中间件
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
登陆时设置session
router.post("/login", (ctx) => {
console.log(ctx.request.body)
const { username, password } = ctx.request.body
if (username === 'yang' && password === "123456") {
// 登陆成功设置session,给session赋值
ctx.session.user = {
username:"yang"
}
ctx.body = {
ok: 1,
}
} else {
ctx.body = {
ok: 0,
}
}
})
JWT
- JWT模块
util/JWT.js:
var jwt = require("jsonwebtoken")
const secret ='yang-secret'
const JWT = {
// 加密
generate(value,expires) {
return jwt.sign(value,secret,{expiresIn:expires})
},
// 解密
verify(token) {
try {
return jwt.verify(token, secret)
} catch (error) {
return false
}
}
}
module.exports = JWT
- 登陆成功设置token
router.post("/login", (ctx) => {
console.log(ctx.request.body)
const { username, password } = ctx.request.body
if (username === 'yang' && password === "123456") {
// 登陆成功设置token,插入到header中
const token = JWT.generate({
_id: "123456",
username:"yang"
}, "10s")
// 将token返回在header中
ctx.set("Authorization",token)
ctx.body = {
ok: 1,
}
} else {
ctx.body = {
ok: 0,
}
}
})
- login.ejs
登录成功将token设置在前端页面上:(axios拦截器)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script type='text/javascript'>
// 拦截器
// 请求发出前执行的方法
axios.interceptors.request.use(function (config) {
console.log("请求发出前执行的方法")
return config;
}, function (error) {
return Promise.reject(error);
});
// 请求成功之后第一个调用的方法
axios.interceptors.response.use(function (response) {
console.log("请求成功之后第一个调用的方法")
const {authorization} = response.headers
authorization && localStorage.setItem("token",authorization)
return response;
}, function (error) {
return Promise.reject(error);
});
</script>
</head>
<body>
<h1>登录界面</h1>
<div>
<div>用户名:<input id="username" /></div>
<div>密码:<input type="password" id="password" /></div>
<div><button id="login">登录</button></div>
</div>
<script type='text/javascript'>
var login = document.querySelector("#login");
var username = document.querySelector("#username");
var password = document.querySelector("#password");
login.onclick = () => {
axios.post("/user/login",{
username: username.value,
password: password.value
}).then(res=>{
if(res.data.ok==1){
console.log(res.data)
// 存储token(拉杰其直接进行了存储)
location.href="/"
}else{
console.log(res)
alert("用户名密码错误")
}
})
};
</script>
</body>
</html>
- index.js
响应请求之前先判断token是否过期
const Koa = require("koa")
const app = new Koa()
const static = require("koa-static")
const path = require("path")
const bodyParser = require('koa-bodyparser')
const views = require("koa-views")
const session = require("koa-session-minimal")
const JWT = require("./utils/jwt")
// 先注册路由级中间件
const router = require("./routes")
//使用ctx.body解析中间件,获取post参数
app.use(bodyParser())
// 配置静态资源
app.use(static(path.join(__dirname,"public")))
// 配置模板引擎
app.use(views(path.join(__dirname, "views"), { extension: "ejs" }))
// token判断拦截
app.use(async (ctx, next) => {
if (ctx.url.includes("login")) {
await next()
return
}
const token = ctx.headers["authorization"]?.split(" ")[1]
if (token) {
const payload = JWT.verify(token)
if (payload) {
// 重新计算token的过期时间
const newToken = JWT.generate({
_id: payload._id,
username: payload.username
}, "1d")
// 将token返回在header中
ctx.set("Authorization",token)
await next()
} else {
ctx.status = 401,
ctx.body = {errCode:-1,errInfo:"token过期"}
}
} else {
await next()
}
})
// 再注册应用级中间件
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
- home.ejs:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script type='text/javascript'>
// 拦截器
// 请求发出前执行的方法
axios.interceptors.request.use(function (config) {
console.log("请求发出前执行的方法")
// 将token给后端
const token = localStorage.getItem("token")
// config是请求对象,可以传递给后端,Bearer 是规范
config.headers.Authorization = `Bearer ${token}`
return config;
}, function (error) {
return Promise.reject(error);
});
// 请求成功之后第一个调用的方法
axios.interceptors.response.use(function (response) {
console.log("请求成功之后第一个调用的方法")
const {authorization} = response.headers
authorization && localStorage.setItem("token",authorization)
return response;
if(response.data.ok==0){
location.href="/login"
}
}, function (error) {
console.log("请求成功之后第一个调用的方法err")
console.log(error.response.status)
if(error.response.status===401){
localStorage.removeItem("token")
location.href="/login"
}
return Promise.reject(error);
});
</script>
<style>
td img{
width: 80px;
height: 80px;
}
</style>
</head>
<body>
<div>
<h1>后台管理系统
<button id="exit">退出登录</button>
</h1>
<br />
<table border="1">
<thead>
<tr>
<td>id</td>
<td>用户</td>
<td>年龄</td>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script type="text/javascript">
var exit = document.querySelector("#exit");
axios.get("/home/list").then((res) => {
res = res.data
console.log(res);
var tbody = document.querySelector("tbody");
// map映射
tbody.innerHTML = res
.map(
(item) => `
<tr>
<td>${item._id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
`
)
.join("");
})
exit.onclick = ()=>{
// 直接移除前端的token
localStorage.removeItem("token")
location.href="/login"
}
</script>
</body>
</html>
koa 文件上传
-
安装 multer 和@koa/multer
npm install --save @koa/multer multer
用法和Express的multer一样。 -
模块配置文章来源:https://www.toymoban.com/news/detail-444510.html
const multer = require("@koa/multer")
// 文件存储地址
const upload = multer({dest:"public/uploads"})
// upload.single("avatar"):接收文件
router.post("/upload", upload.single("avatar"),(ctx) => {
console.log(ctx.request.body,ctx.file)
ctx.body={ok:1}
})
koa 操作MongoDB
- 下载模块:
npm i mongoose
- 启动MongoDB
- 在vscode中配置mongoDB
db.config.js:
// 连接数据库
const mongoose = require("mongoose")
mongoose.connect("mongodb://127.0.0.1:27017/yang_project")
// 插入集合和数据,yang_project会自动创建
- index.js引入
// 链接数据库
require("./config/db.config")
- 向数据库中存数据
// upload.single("avatar"):接收文件
router.post("/upload", upload.single("avatar"),async (ctx) => {
console.log(ctx.request.body, ctx.file)
const { username, password, age } = ctx.request.body
const avatar = ctx.file ? `/uploads/${ctx.file.filename}` : `/uploads/9f774d77e9072c2912efb7528249db42`
// 利用UserModel模型进行存储操作 UserModel.create
await UserModel.create({
username,
age,
password,
avatar
})
ctx.body={ok:1}
})
代码获取:
链接:https://pan.baidu.com/s/1rzovC9h5Nl3lc-XXvRdptw?pwd=xtfx
提取码:xtfx文章来源地址https://www.toymoban.com/news/detail-444510.html
到了这里,关于koa框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!