携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情
Koa 入门
什么是 Koa
- koa是基于nodejs平台的下一代web开发框架
- express原班人马打造,更精简
- Async + await 处理异步
- 洋葱圈型的中间件机制
创建一个最简单的 koa
初始化一个 npm 项目
npm init -y
安装 koa 依赖 npm install koa --save
根目录下 exampleApp.js
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
// ctx 是封装了 request 和 response 的上下文
ctx.body = 'hello koa'
// next 是下一个中间件
})
app.listen(3000)
根目录下运行 node exampleApp.js,浏览器输入 localhost:3000,输出 hello koa,服务已经启动成功。
const Koa = require('koa')
const app = new Koa()
// 135642
app.use(async (ctx, next) => {
ctx.body = '1'
next()
ctx.body = ctx.body + '2'
})
app.use(async (ctx, next) => {
ctx.body += '3'
next()
ctx.body += '4'
})
app.use(async (ctx, next) => {
ctx.body += '5'
next()
ctx.body += '6'
})
// app.use(async (ctx) => {
// ctx.body = 'hello koa'
// })
app.listen(3000)
浏览器输出 135642
若将 第一个的 next() 注释掉,则只会输出 12
next() 的作用就是执行下面的中间件
解决异步的情况
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
ctx.body = '1'
setTimeout(() => {
next()
}, 2000)
ctx.body = ctx.body + '2'
})
app.use(async (ctx, next) => {
ctx.body += '3'
next()
ctx.body += '4'
})
app.use(async (ctx, next) => {
ctx.body += '5'
next()
ctx.body += '6'
})
// app.use(async (ctx) => {
// ctx.body = 'hello koa'
// })
app.listen(3000)
此时浏览器就是输出 12
const Koa = require('koa')
const app = new Koa()
// 135642
function delay () {
return new Promise((reslove, reject) => {
setTimeout(() => {
reslove()
}, 2000)
})
}
app.use(async (ctx, next) => {
ctx.body = '1'
await next()
ctx.body += '2'
})
app.use(async (ctx, next) => {
ctx.body += '3'
await delay()
await next()
ctx.body += '4'
})
app.use(async (ctx, next) => {
ctx.body += '5'
await next()
ctx.body += '6'
})
// app.use(async (ctx) => {
// ctx.body = 'hello koa'
// })
app.listen(3000)
此时浏览器输出 135642
对于请求的处理和响应
koa-router(路由处理)npm install koa-router --save
const Koa = require('koa')
const Router = require('koa-router')
// 实例化
const app = new Koa()
const router = new Router()
router.get('/', async (ctx) => {
ctx.body = 'home'
})
router.get('/news', async (ctx) => {
ctx.body = 'news'
})
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
Get传值
// 假设前端的请求路径 http://localhost:3000/newsContent?aid=123&name=zs
router.get('/newsContent', async (ctx) => {
// 推荐方式
// 获取路由传递的参数
console.log(ctx.query)
// { aid: '123', name: 'zs' }
console.log(ctx.querystring)
// aid=123&name=zs
// console.log(ctx.request)
/**
* {
method: 'GET',
url: '/newsContent?aid=123&name=zs',
header: {
host: 'localhost:3000',
connection: 'keep-alive',
'cache-control': 'max-age=0',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
'sec-fetch-user': '?1',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,im
age/apng,*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en,zh;q=0.9,zh-CN;q=0.8'
}
}
*/
console.log(ctx.request.url)
// /newsContent?aid=123&name=zs
console.log(ctx.request.query)
//[Object: null prototype] { aid: '123', name: 'zs' }
console.log(ctx.request.querystring)
//aid=123&name=zs
ctx.body = '新闻详情'
})
动态匹配
// http://localhost:3000/page/112
router.get('/page/:id', async (ctx) => {
console.log(ctx.params)
// {id: 112}
ctx.body = '新闻详情'
})
// http://localhost:3000/pages/112/qwe
router.get('/pages/:id/:bid', async (ctx) => {
console.log(ctx.params)
// { id: '112', bid: 'qwe' }
ctx.body = '新闻详情'
})
koa 中间件应用
var Koa = require('koa')
var Router = require('koa-router')
var app = new Koa()
var router = new Router()
// 应用中间件
/**
* 会匹配任何的路由,无论访问任何的路由,都会打印出 middleware
*/
// 无 next(),匹配终止
app.use(async (ctx) => {
ctx.body = 'middleware'
})
// 有 next() 就会接着向下执行
app.use(async (ctx, next) => {
console.log(new Date())
// 2020-04-06T01:53:02.529Z
//2020-04-06T01:53:10.619Z
await next()
})
router.get('/', async (ctx) => {
console.log('home 01')
ctx.body = 'home'
})
router.get('/news', async (ctx) => {
console.log('news')
ctx.body = 'news'
})
router.get('/login', async (ctx) => {
ctx.body = 'login'
})
// 应用中间件
// 路由中间件
router.get('/pages', async (ctx, next) => {
console.log('这是一个页面1')
// 没有 await next (), ctx.body 没有值 界面会显示 Not Found
// 程序没有办法继续执行
await next()
// next() 会继续向下匹配,之后在界面打印出 page,控制台也会输出 这是一个页面1
})
router.get('/pages', async (ctx) => {
ctx.body = 'page'
})
// 中间件的执行与放置的位置无关
router.get('/', async (ctx) => {
console.log('home 01')
ctx.body = 'home'
})
router.get('/news', async (ctx) => {
console.log('news')
ctx.body = 'news'
})
router.get('/login', async (ctx) => {
ctx.body = 'login'
})
app.use(async (ctx, next) => {
console.log('middleware 01')
next()
/**
* middleware 01
home 01
*/
/**
* 无论放置在哪里,都会先执行中间件,之后继续匹配路由
*/
})
// 中间件的执行与放置的位置无关
// 寻找 404 界面
router.get('/', async (ctx) => {
console.log('home 01')
ctx.body = 'home'
})
router.get('/news', async (ctx) => {
console.log('news')
ctx.body = 'news'
})
router.get('/login', async (ctx) => {
ctx.body = 'login'
})
app.use(async (ctx, next) => {
console.log('middleware 01')
next()
/**
* middleware 01
home 01
*/
/**
* 无论放置在哪里,都会先执行中间件,之后继续匹配路由
*/
if(ctx.status === 404) {
ctx.status = 404
ctx.body = '404 page'
} else {
console.log(ctx.url)
}
/**
* 访问 /news 路由
* 控制台输出
* middleware 01
news
/news
1.先执行中间件 输出 middleware 01
2.存在 next (), 继续向下匹配路由
3.存在 /news 界面,控制台输出 news
4.ctx.body 有值,控制台输出 /news
访问 /*** 路由
1.先执行中间件 输出 middleware 01
2.存在 next (), 继续向下匹配路由
3.匹配不到,ctx.body = 404
4.返回 404 page
*/
})
// 中间件的执行顺序
app.use(async (ctx, next) => {
console.log('middleware 01')
await next()
console.log('return middleware 01')
})
app.use(async (ctx, next) => {
console.log('middleware 02')
await next()
console.log('return middleware 02')
})
router.get('/news', async (ctx) => {
console.log('匹配到 news page')
ctx.body = 'news'
})
/**
* middleware 01
middleware 02
匹配到 news page
return middleware 02
return middleware 01
1.先执行中间件 输出 middleware 01
2.遇到 next() ,继续执行
3.碰到中间件 输出 middleware 02
4.无中间件 匹配路由,匹配到 /news
5.输出 匹配到 news page
6. ctx.body 有值,页面输出 news
7.返回最后执行的中间件 ,输出 return middleware 02
8.接着返回 输出 return middleware 01
*/
// 中间件的执行顺序
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
ejs 模板
npm install koa-views --save npm install ejs --save
var Koa = require('koa')
var Router = require('koa-router')
var views = require('koa-views')
var app = new Koa()
var router = new Router()
// 配置模板引擎
app.use(views(__dirname + '/'), {
map: {
html: 'ejs'
}
})
router.get('/', async (ctx) => {
await ctx.render('./views/index.ejs')
})
router.get('/news', async (ctx) => {
ctx.body = 'news'
})
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
post 请求
npm install koa-bodyparser --save
var Koa = require('koa')
var Router = require('koa-router')
var views = require('koa-views')
var bodyParser = require('koa-bodyparser')
var app = new Koa()
var router = new Router()
// 配置模板引擎
app.use(views(__dirname + '/'), {
map: {
html: 'ejs'
}
})
app.use(bodyParser())
router.get('/', async (ctx) => {
await ctx.render('./views/index.ejs')
})
router.post('/doAdd', async (ctx, next) => {
// 获取表单提交数据
ctx.body = ctx.request.body
console.log(ctx.body)
// { username: 'zasss', password: '123456' }
})
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
静态资源
npm install koa-static --save
var Koa = require('koa')
var Router = require('koa-router')
var views = require('koa-views')
var bodyParser = require('koa-bodyparser')
// 引入
const static = require('koa-static')
var app = new Koa()
var router = new Router()
app.use(views(__dirname + '/'), {
map: {
html: 'ejs'
}
})
app.use(bodyParser())
// 静态资源
app.use(static('static'))
// app.use(static('public'))
// 静态资源 可以配置多个
router.get('/', async (ctx) => {
await ctx.render('./views/index.ejs')
})
router.post('/doAdd', async (ctx, next) => {
ctx.body = ctx.request.body
console.log(ctx.body)
})
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
koa-cookie
/**
* 1 保存用户信息
* 2 浏览器历史记录
* 3 猜你喜欢的功能
* 4 10天免登陆
* 5 多个页面之间的数据传递
* 6 cookie 实现购物车功能
*/
var Koa = require('koa')
var Router = require('koa-router')
var views = require('koa-views')
var bodyParser = require('koa-bodyparser')
// 引入
const static = require('koa-static')
var app = new Koa()
var router = new Router()
app.use(views(__dirname + '/'), {
map: {
html: 'ejs'
}
})
app.use(bodyParser())
// 静态资源
app.use(static('static'))
// app.use(static('public'))
// 静态资源 可以配置多个
// 设置 cookie
router.get('/', async (ctx) => {
// 访问 / 路由时设置 cookies
// maxAge: 设置过期时间
// path 只有访问特定的路由才可以访问
// domin 域名
// httpOnly 只有服务器端才可以访问
ctx.cookies.set('userInfo', 'zhangsan', {
maxAge: 60 * 1000 * 60,
// expires: '2020-04-07',
// path: '/news',
// domin: '',
// httpOnly: true
})
await ctx.render('./views/index.ejs')
})
router.get('/news', async (ctx) => {
// 访问 /news 路由的时候,可以取到相对应的 cookies
console.log(ctx.cookies.get('userInfo'))
await ctx.render('./views/index.ejs')
})
router.get('/login', async (ctx) => {
// 访问 /login 路由的时候,可以取到相对应的 cookies
console.log(ctx.cookies.get('userInfo'))
ctx.body = ctx.cookies.get('userInfo')
// await ctx.render('./views/index.ejs')
})
// 设置 cookie
// 设置中文 cookie
router.get('/', async (ctx) => {
// value 设置为中文 argument value is invalid
// 无法访问
// koa 中 设置中文 Cookies
var name = new Buffer('张三').toString('base64')
ctx.cookies.set('userInfo', name, {
maxAge: 60 * 1000 * 60,
})
await ctx.render('./views/index.ejs')
})
router.get('/news', async (ctx) => {
// 访问 /news 路由的时候,可以取到相对应的 cookies
var data = ctx.cookies.get('userInfo')
var userinfo = new Buffer(data, 'base64').toString()
console.log(userinfo)
// console.log(ctx.cookies.get('userInfo'))
await ctx.render('./views/index.ejs')
})
// 设置中文 cookie
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)
koa-session
/**
* session 是一种记录客户状态的机制
* 存储在服务端的
*/
var Koa = require('koa')
var Router = require('koa-router')
var views = require('koa-views')
var bodyParser = require('koa-bodyparser')
// 引入
const static = require('koa-static')
const session = require('koa-session')
var app = new Koa()
var router = new Router()
app.use(views(__dirname + '/'), {
map: {
html: 'ejs'
}
})
app.use(bodyParser())
// 静态资源
app.use(static('static'))
// app.use(static('public'))
// 静态资源 可以配置多个
app.keys = ['some secret hurr']
const CONFIG = {
key: 'koa:sess',
maxAge: 9000, // 过期时间
autoCommit: true,
overwrite: true,
httpOnly: true,
signed: true,
rolling: false,
renew: true,
sameSite: null,
}
app.use(session(CONFIG, app))
router.get('/', async (ctx) => {
console.log(ctx.session.userInfo)
await ctx.render('./views/index.ejs')
})
router.get('/login', async (ctx) => {
ctx.session.userInfo = '张三'
await ctx.render('./views/index.ejs')
})
router.get('/page', async (ctx, next) => {
console.log(ctx.session.userInfo)
})
//启动路由
app.use(router.routes()).use(router.allowedMethods())
// 设置响应头
// 监听在3000端口
app.listen(3000)