koa2官网: https://koa.bootcss.com/
一. 安装koa2
Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.
cnpm i koa --save
二. hello world
const Koa = require('koa')//引入Koa
const app = new Koa(); //实例化Koa
app.use(async (ctx) => { //使用app
ctx.body = 'hola' //ctx就是context上下文,是Koa自带的对象
})
app.listen(3000);
context是什么?
我们先查看下ctx对象Koa Context将节点的请求和响应对象封装到单个对象中,该对象为编写Web应用程序和API提供了许多有用的方法。
简单来说:
-
ctx.url
是Koa请求的路径 -
ctx.request
是Koa请求对象; -
ctx.response
是Koa响应对象 -
ctx.body
body是http协议中的响应体 ctx.body = ctx.res.body = ctx.response.body -
ctx.req
是原生nodejs.request请求对象 -
ctx.res
是原生nodejs.response响应对象 -
ctx.method
是Koa的请求方法
想了解上下文context,请看文章:https://www.jianshu.com/p/9488cd3feaf9
三. 接收get请求接收
Koa接收的get请求会存入上下文中,我们只需要从上下文中通过ctx.request
取出即可
const Koa = require("koa")
const app = new Koa();
app.use(
async (ctx) => {
let url = ctx.url;
let request = ctx.request;
let req_query = req.query;
let req_querystring = req.querystring;
ctx.body = {
url,
req_query,
req_querystring
}
}
)
app.listen(
3000, () => {
console.log('running');
}
)
其中ctx.request.query
是请求中的query对象,
其中
ctx.request.querystring
是请求中的query字符串,
除了使用
ctx.request.query
和ctx.request.querystring
之外,问了方便使用,koa还允许用ctx.query
ctx.querystring
这样的简写
四. Post请求接收
对于POST请求的处理,Koa2没有封装方便的获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req来获取。
1. 原生方法获取Post请求的步骤:(仅为了学习原理)
- 解析上下文ctx中的原生nodex.js对象req。
- 将POST表单数据解析成query string-字符串.(例如:user=jspang&age=18)
- 将字符串转换成JSON格式。
const Koa = require("koa")
const app = new Koa();
app.use(
async (ctx) => {
if (ctx.url === '/' && ctx.method === "GET") {
ctx.body = ` haha
`
} else if (ctx.url === '/' && ctx.method === "POST") {
let postdata = await parsePost(ctx) //注意是异步调用
ctx.body = postdata
} else {
ctx.body = '404'
}
}
)
function parsePost(ctx) { //解析ctx里的post
return new Promise((resolve, reject) => {
try {
let postdata = '';
ctx.req.addListener('data', (data) => { //ctx.req添加监听事件监听data的变化
postdata = data.toString(); //接收新的data并转为字符串
parseQueryStr(postdata);//将字符串转为JSON对象
})
ctx.req.on('end', function () { //ctx.req添加监听事件监听end
resolve(parseQueryStr(postdata))
})
} catch (error) {
reject(error)
}
})
}
function parseQueryStr(queryStr) {//此函数用于将Post字符串转化为对象
let queryData = {}
let queryStrList = queryStr.split('&')
for (let [index, queryStrInList] of queryStrList.entries()) {
let itemList = queryStrInList.split('=')
queryData[itemList[0]] = decodeURIComponent(itemList[1])
}
return queryData
}
app.listen(
3000, () => {
console.log('running');
}
)
2. 使用中间件 koa-bodyparser
(1).安装
cnpm i koa-bodyparser --save
(2). 使用
//引入
const bodyparser=require('koa-bodyparser')
//使用
app.use(bodyparser())
经过上面的步骤后,当有post请求时,koa-bodyparser会自动将解析好的对象放入 ctx.request.body
之中
上面的程序可以改成:
const Koa = require("koa")
const bodyparser = require('koa-bodyparser')
const app = new Koa();
app.use(bodyparser())
app.use(
async (ctx) => {
if (ctx.url === '/' && ctx.method === "GET") {
ctx.body = ` haha
`
} else if (ctx.url === '/' && ctx.method === "POST") {
let postdata = ctx.request.body //注意是异步调用
ctx.body = postdata
} else {
ctx.body = '404'
}
}
)
app.listen(
3000, () => {
console.log('running');
}
)
五. 路由
1. 原生路由 (仅为了学习原理)
const Koa = require("koa")
const fs = require('fs')
const app = new Koa();
app.use(
async (ctx) => {
let html = await myrouter(ctx.url);
ctx.body = html
}
)
async function myrouter(url) {
let page = '404.html'
switch (url) {
case '/':
page = 'index.html'
break;
case '/index':
page = 'index.html'
break;
case '/todo':
page = 'todo.html'
break;
default:
break;
}
let html = await myrender(page);
return html
}
async function myrender(page) {
return new Promise((resolve, reject) => {
fs.readFile(`./page/${page}`, 'binary', (err, data) => {
if (err) reject(err);
resolve(data);
})
})
}
app.listen(
3000, () => {
console.log('running');
}
)
2. 使用中间件 koa-router
cnpm install koa-router --save
(1). 基本使用方法
//1. 引入
const Router = require('koa-router')
//2. 实例化
const router = new Router();
//3. 写请求处理
router.get('/', (ctx, next) => {
ctx.body='hola'
})
//4. app用起来
app.use(router.routes())
例:
const Koa = require("koa")
const fs = require('fs')
const Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.get('/', (ctx, next) => {
ctx.body = 'hola'
})
.get('/todo', (ctx, next) => {
ctx.body = 'todos'
})
app
.use(router.routes())
.use(router.allowedMethods())
app.listen(
3000, () => {
console.log('running');
}
)
我们把原生的例子改为用koa-router
const Koa = require("koa")
const fs = require('fs')
const Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.get('/', async (ctx, next) => {
ctx.body = await getPage('index')
})
.get(['/todo', '/index'], async (ctx, next) => {
ctx.body = await getPage(ctx.url)
})
.get('*', async (ctx, next) => {
ctx.body = await getPage(404)
})
async function getPage(url) {
return new Promise((resolve, reject) => {
fs.readFile(`./page/${url}.html`, 'binary', (err, data) => {
if (err) reject(err)
console.log(data);
resolve(data)
})
})
}
app
.use(router.routes())
.use(router.allowedMethods())
app.listen(
3000, () => {
console.log('running');
}
)
(2). 路由的层级
①. 前缀
给路由直接增加一个层级
const router = new Router({
prefix:'/hola'
});
这样一来, 原先 /xxx访问的页面 要/hola/xxx 来访问了~~
③. 主路由子路由分离
- 分别实例化父路由及子路由
- 配置子路由
- 父路由挂载子路由
const Koa = require("koa")
const Router = require('koa-router')
const app = new Koa();
const router = new Router(); //父路由
const home = new Router(); //子路由
const page = new Router(); //子路由
home
.get('/', async (ctx, next) => {
ctx.body = '/home/index'
})
.get('/todo', async (ctx, next) => {
ctx.body = '/home/todo'
})
page
.get('/', async (ctx, next) => {
ctx.body = '/page/index'
})
.get('/todo', async (ctx, next) => {
ctx.body = '/page/todo'
})
router.use('/home', home.routes(), home.allowedMethods())
router.use('/page', page.routes(), page.allowedMethods())
app
.use(router.routes())
.use(router.allowedMethods())
app.listen(
3000, () => {
console.log('running');
}
)
六. cookie
cookie也被封装进了ctx
cookie的读: ctx.cookies.get('键')
cookie的写: ctx.cookies.set('键','值',{配置})
const Koa = require("koa")
const app = new Koa();
app.use(
async (ctx) => {
if (ctx.url === '/index') {
ctx.cookies.set(
'MyName', 'lili', { //cookie的键值
domain: ['127.0.0.1'], //cookie的域名信息
maxAge: 1000 * 60 * 60 * 24 * 10, //最大有效事件 单位毫秒
expires: new Date('2020-05-01'), //失效时间
httpOnly: false, //只允许http请求时才有效,一般填false
overwrite: false //允许重写? 一般填false
}
)
ctx.body = 'Cookie is Ok'
} else {
if (ctx.cookies.get('MyName')) {
ctx.body = ctx.cookies.get('MyName')
} else {
ctx.body = 'cookie is not ready'
}
}
}
)
app.listen(
3000, () => {
console.log('running');
}
)
七. 模板引擎 koa-views ejs
cnpm i koa-views --save
cnpm i ejs --save
const Koa = require("koa")
const views = require('koa-views')
const path = require('path')
const app = new Koa();
app.use(views(path.join(__dirname, './view'), {
extension: 'ejs'
}))
app.use(
async (ctx) => {
await ctx.render('index', {
title: 'hola~你好啊!'
})
}
)
app.listen(
3000, () => {
console.log('running');
}
)
八. 引用静态资源
cnpm i koa-static --save
- 引入中间件
- 设置好公开的静态资源目录
- app用起来
const Koa = require("koa")
const path = require('path')
const static = require('koa-static') //引入静态资源中间件
const app = new Koa();
const staticPath = './static' //要开放的静态资源文件夹
app.use(static(path.join(__dirname, staticPath)))
app.use(
async (ctx) => {
ctx.body = 'hello world'
}
)
app.listen(
3000, () => {
console.log('running');
}
)