Koa2基础学习

搭建koa开发环境

koa基于async,需要node版本7.6以上

  1. 新建一个文件夹并进入
  2. 生成package.json文件
    1
    npm init -y
  3. 安装koa(–save安装到生产环境)
    1
    npm i --save koa
  4. 新建一个文件index.js
    1
    2
    3
    4
    5
    6
    7
    8
    const Koa = require('koa')
    const app = new Koa()
    // 异步语法糖 ctx上下文
    app.use(async(ctx)=>{
    ctx.body = 'hello,leni'
    })
    app.listen(3000)
    console.log('app is starting at port 3000')
    终端执行
    1
    node index.js
    就可以在浏览器输入127.0.0.1:3000访问了。

async和await的使用

  1. 新建一个文件demo01.js, 普通函数加上async就变成异步的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function getSth(){
    return 'something'
    }

    async function testAsync(){
    return 'hello,async'
    }

    async function test(){
    const v1 = await getSth()
    const v2 = await testAsync()
    console.log(v1,v2)
    }

    test() // something hello async 说明await不仅能接收异步的,普通函数返回的结果也可以

    const result = testAsync()
    console.log(result) // 返回一个promise
    await会造成阻塞,所以必须放在异步方法里面,不然程序可能会崩溃
    所以await两个特点
  • 必须放在异步方法里面
  • 不仅能接收异步的,普通函数返回的结果也可以

get请求数据的接收

新建一个文件demo02.js
在koa2中get请求通过request接收,但是接收的方法有两种:query和querystring

  • query:返回的是格式化好的参数对象
  • querystring:返回的是请求字符串

koa也可以不用request接收请求, 见demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx)=>{
let url = ctx.url
// 从request中接收Get请求
let request = ctx.request
let req_query = request.query
let re_querystring = request.querystring

//从上下文中直接Get请求
let ctx_query = ctx.query
let ctx_querystring = ctx.querystring

ctx.body = {
url,
req_query,
req_querystring,
ctx_query,
ctx_querystring
}
})
app.listen(3000,()=>{
console.log('[demo] is starting at port 3000')
})

访问127.0.0.1:3000带参数
127.0.0.1:3000?name=leni&age=18
(可以安装iformattool插件)


Post请求如何接收

针对post请求,koa没有封装方便的获取参数的方法,需要通过解析上下文context中原生node.js请求对象req来获取。

获取Post请求的步骤:

  1. 解析上下文ctx中的原生node.js的req
  2. 将Post表单数据解析成query string字符串
  3. 将字符串转换为JSON格式

    ctx.request和ctx.req的区别

  • ctx.request:是koa2中contex经过封装的请求对象,用起来比较直观简单
  • ctx.req:是context提供的node.js原生Http请求对象。不直观,有更多的内容,但是适合深度编程

新建一个文件demo03.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const Koa = require('koa')
const app = new Koa()

app.use(async(ctx)=>{
if(ctx.url === '/' && ctx.method === 'GET'){
// 显示表单
let html = `
<h1>leni Koa2 request Post</h1>
<form method='POST' action='/'>
<p>userName</p>
<input name='userName'/><br/>
<p>age</p>
<input name='age'/><br/>
<button type='submit'>submit</button>
</form>
`
ctx.body = html
}else if(ctx.url === '/' && ctx.method === 'POST'){
ctx.body = '接收到post参数'
}else{
ctx.body = '<h1>404</h1>'
}

})

app.listen(3000,()=>{
console.log('[demo03] is starting at port 3000')
})

解析node原生post参数

用promise对象进行解析,使用ctx.req.on来接收事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function parsePostData(ctx){
    return new Promise((resolve,reject)=>{
        try {
            let postData = ''
            ctx.req.on('data',data=>{
                postData += data
            })
            ctx.req.addListener('end'function(){
                resolve(postData)
            })
        } catch (error) {
            reject(error)
        }
    })
}

然后在接收post请求的处理方法里,修改代码:

1
2
 let postData = await parsePostData(ctx)
        ctx.body = postData

POST字符串解析为JSON对象

写一个字符串封装成JSON对象的方法

1
2
3
4
5
6
7
8
9
function parseQueryStr(queryStr){
    let queryData = {}
    let queryStrList = queryStr.split('&')
    for(let item of queryStrList){
        let itemList = item.split('=')
        console.log(itemList)
        queryData[itemList[0]] = decodeURIComponent(itemList[1])
    }
}

Koa-bodyparser中间件

安装

1
npm i --save koa-bodyparser@3

新建一个文件demo04.js,复用上一个demo代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const Koa = require('koa')
const app = new Koa()
const bodyparser = require('koa-bodyparser')

app.use(bodyparser())

app.use(async(ctx)=>{
    if(ctx.url === '/' && ctx.method === 'GET'){
        // 显示表单
        let html = `
            <h1>leni Koa2 request Post</h1>
            <form method='POST' action='/'>
                <p>userName</p>
                <input name='userName'/><br/>
                 <p>age</p>
                <input name='age'/><br/>
                <button type='submit'>submit</button>
            </form>
        `
        ctx.body = html
    }else if(ctx.url === '/' && ctx.method === 'POST'){
        // ctx.body = '接收到post参数'
        let postData = ctx.request.body
        ctx.body = postData
    }else{
        ctx.body = '<h1>404</h1>'
    }
    
})

app.listen(3000,()=>{
    console.log('[demo03] is starting at port 3000')
})

koa2原生路由实现

新建一个文件demo05.js, 新建一个文件夹page(放404.html,index.html,todo.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const Koa = require('koa')
const app = new Koa()
const fs = require('fs') // 实现流工作

function render(page){
return new Promise((resolve,reject)=>{
let pageUrl = `./page/${page}`
fs.readFile(pageUrl, 'binary', (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}

async function route(url){
let page = '404.html'
switch(url){
case '/':
page = 'index.html'
break;
case '/index':
page = 'index.html'
break;
case '/todo':
page = 'todo.html'
break;
case '/404':
page = '404.html'
break;
default:
break;
}
let html =await render(page)
return html;
}

app.use(async(ctx)=>{
let url = ctx.request.url
let html =await route(url)
ctx.body = html
})

app.listen(3000)

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!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>
    <h1>Koa demo Index Page</h1>
    <p>this is Index page</p>
    <ul>
        <li><a href="/">/</a></li>
        <li><a href="/index">index</a></li>
        <li><a href="/todo">todo</a></li>
        <li><a href="/404">404</a></li>
    </ul>
</body>
</html>

todo.html

1
2
3
4
5
6
7
8
9
10
11
12
<!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>
    <h1>Koa demo Todo Page</h1>
    <p>this is todo page</p>
</body>
</html>

404.html

1
2
3
4
5
6
7
8
9
10
11
12
<!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>
    <h1>Koa demo 404 Page</h1>
    <p>this is 404 page</p>
</body>
</html>

Koa-router中间件(1)

安装

1
npm i --save koa-router

新建文件demo06.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
const router = new Router()
router
    .get('/', (ctx, next)=>{
        ctx.body = 'index page'
    })
    .get('/todo', (ctx, next)=>{
        ctx.body = 'todo page'
    })
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000,()=>{
    console.log('[demo06] is starting at port 3000')
})

Koa-router中间件 (2) 层级

新建文件demo07.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()

let home = new Router()
home.get('/leni', async(ctx)=>{
ctx.body = 'Home leni page'
}).get('/todo', async(ctx)=>{
ctx.body = 'Home todo page'
})

let page = new Router()
page.get('/leni', async(ctx)=>{
ctx.body = 'Page leni page'
}).get('/todo', async(ctx)=>{
ctx.body = 'Page todo page'
})

// 父级路由, 装载所有子路由
const router = new Router()
router.use('/home', home.routes(), home.allowedMethods())
.use('/page', page.routes(), page.allowedMethods())

// 加载路由中间件
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000,()=>{
    console.log('[demo07] is starting at port 3000')
})

Koa-router中间件 (3) 参数

新建文件demo08.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Koa = require('koa')
const Router = require('koa-router')

const app = new Koa()
const router = new Router()

router.get('/', (ctx,next)=>{
ctx.body = ctx.query
})

app.use(router.routes()).use(router.allowedMethods())

app.listen(3000,()=>{
console.log('[demo08] is starting at port 3000')
})

cookie的使用

新建文件demo09.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx)=>{
   if(ctx.url === '/index'){
        ctx.cookies.set(
            'MyName''Leni', {
                domain: '127.0.0.1',
                path: '/'// 改为/index的话,只有路径为/index时才能访问到
                maxAge: 1000*60*60*24,
                expires: new Date('2020-03-01'),
                httpOnly: false,
                overwirte: false
            }
        )
        ctx.body = 'Cookie is ok'
   }else{
        if(ctx.cookies.get('MyName')){
            ctx.body = ctx.cookies.get('MyName')
        }else{
            ctx.body = 'hello, leni'
        }
   }
})
app.listen(3000,()=>{
    console.log('[demo09] is starting at port 3000')
})

ejs模板

安装中间件

1
npm i --save koa-views

安装ejs模板引擎

1
npm i --save ejs

编写模板,为了模板同一管理,新建文件夹view(含index.ejs)
views/index.ejs

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
</head>
<body>
    <h1><%= title %></h1>
    <p>EJS Welcome to <%= title %></p>
</body>
</html>

新建文件demo10.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Koa = require('koa')
const app = new Koa()
const path = require('path')
const views = require('koa-views')
app.use(views(path.join(__dirname,'./views'),{
    extension: 'ejs'
}))
app.use(async(ctx)=>{
    let title = 'hello, koa2'
    await ctx.render('index', {
        title
    })
})
app.listen(3000,()=>{
    console.log('[dmeo10] is starting at port 3000')
})

koa-static静态资源中间件

安装koa-static

1
npm i --save koa-static

新建static文件夹,放入静态资源
新建文件demo11.js

1
2
3
4
5
6
7
8
9
10
11
12
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('[dmeo11] is starting at port 3000')
})

访问127.0.0.1:3000/index.js(资源文件名)就可以访问静态资源了


补充

app.use(function): 将给定的中间件方法添加到此应用程序中。
demo地址:https://github.com/leniNikky/koa-simple-demo