koa2 相关内容

koa2是express原班人马开发的,相对于express来说koa2的语法更加简单,给开发人员一种特别清新的感觉

Koa
Koa

环境要求

  • 因为node.js v7.6.0开始完全支持async/await,不需要加flag,所以node.js环境都要7.6.0以上
  • npm 版本3.x以上

    安装Koa

    新建一个文件夹作为koa的学习目录,然后进入当前目录下打开命令行执行一下命令:
    1
    2
    npm init
    npm install --save koa

1.开始第一个koa应用

1
2
3
4
5
6
7
8
const Koa = require('koa')
const app = new Koa()
const main = async( ctx ) => {
ctx.body = 'hello world'
}
app.use(main())
app.listen(3000)
console.log('[demo] start-quick is starting at port 3000')

打开浏览器,在地址栏输入localhost:3000 会车看一下效果,你已经启动一个koa的服务:

image
image

如果大家对async await的用法还是不熟悉的话可以看在下JavaScript 异步函数 async await 介绍这篇文章

2.路由

1.koa的原生路由

说到koa的原生路由,特别简单,易懂,就是写起来比较麻烦

1
2
3
4
5
6
7
8
9
10
11
12
const Koa = require('koa')
const app = new Koa()

const main = async(cxt)=>{
if(cxt.request.path === '/'){
cxt.body = '首页'
}else if(cxt.request.path === 'about'){
cxt.body = '关于我们'
}else{
cxt.body = '404'
}
}

1.中间件koa-router

  • 安装koa-router

    1
    npm install --save koa-router
  • 使用koa-router

    1
    2
    const Router = require('koa-router');// 导入
    const router = new koaRouter();// 注意koa-router是一个构造函数我们使用它创造router实例
  • router实例属性及其参数
    router实例的属性与浏览器的请求方式相同get,post,put,del,all。两个必传参数,第一个参数是你要应对的请求路径,第二个参数是当用户请求该路径你要处理的逻辑函数,逻辑函数的参数与koa中间件的参数一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    router
    .get('/', (ctx, next) => {
    ctx.body = 'Hello World!';
    })
    .post('/users', (ctx, next) => {
    // ...
    })
    .put('/users/:id', (ctx, next) => {
    // ...
    })
    .del('/users/:id', (ctx, next) => {
    // ...
    })
    .all('/users/:id', (ctx, next) => {
    // ...
    });
  • 路由可以有具体的名称。这允许在开发期间生成URL并轻松重命名URL。

1
2
3
4
5
6
router.get('user', '/users/:id', (ctx, next) => {
// ...
});

router.url('user', 3);
// => "/users/3"
  • 在一个路由里面你可以使用多个中间件
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()
const Router = require('koa-router')
const router = new Router()
router.get(
'/',
(ctx, next) => {
console.log(111)
next();
console.log(222)
},
(ctx, next) => {
console.log(333)
next();
console.log(444)
},
(ctx, next) => {
console.log(555)
next();
console.log(666)
}
);
app.use(router.routes());
app.listen(3000);

注意: 中间件的执行顺序是这样的
111>
333>
555>
666>
444>
222

router.routes()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router()
app.use(router.routes());

router.get('/', (cxt, next)=>{
cxt.body = '首页'
})

router.get('/about', (cxt, next)=>{
cxt.body = '关于'
})

router.get('/all', (cxt, next)=>{
cxt.body = '我们'
})

app.use(router.routes());
app.listen(3500);

router.routes()函数会将所有的路由归纳生成一个函数对象,该对象的router属性下的stack属性是一个数组,存放着路由规则,在执行过程中和堆栈一样先进后出 如图:

image
image

3.获取请求数据

GET请求

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

app.use( async ( ctx ) => {
let url = ctx.url
// 从上下文的request对象中获取
let request = ctx.request
let req_query = request.query
let req_querystring = request.querystring

// 从上下文中直接获取
let ctx_query = ctx.query
let ctx_querystring = ctx.querystring

ctx.body = {
url,
req_query,
req_querystring,
ctx_query,
ctx_querystring
}
})
app.listen(3000);

结果如图:我们可以通过请求对象ctx.request获取请求参数,也可以通过ctx获取。

image
image

POST请求

说到处理POST请求,我们要说到一个模块koa-body,koa-body可以处理表单提交,也就是 POST 方法发送到服务器的键值对。

1
2
3
4
5
6
7
8
9
10
11
12
13
const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();

const main = async function(ctx) {
const body = ctx.request.body;
if (!body.name) ctx.throw(400, '.name required');
ctx.body = { name: body.name };
};

app.use(koaBody());
app.use(main);
app.listen(3000);

在命令行中端输入如下命令,模拟一个POST请求,你会看到终端成功请求后返回的数据。

1
curl -X POST --data "name=Jack" 127.0.0.1:3000 {"name":"Jack"}

koa-body 的使用

koa-body 不仅可以处理web客户端发送的POST请求参数,而且可以处理文件上传,文件下载等流问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const os = require('os');
const path = require('path');
const koaBody = require('koa-body');

const main = async function(ctx) {
const tmpdir = os.tmpdir();
const filePaths = [];
const files = ctx.request.body.files || {};

for (let key in files) {
const file = files[key];
const filePath = path.join(tmpdir, file.name);
const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);
filePaths.push(filePath);
}

ctx.body = filePaths;
};

app.use(koaBody({ multipart: true }));

启动这个js

1
$ node [name].js

新建一个终端输入如下命令:

1
$ curl --form upload=@/path/to/file http://127.0.0.1:3000 ["/tmp/file"]

koa-static中间件使用

1. 简介

koa-static 是为客户端提供类似图片,文字等静态资源文件的中间件

2. 用法

1
2
3
4
const Koa = require('koa');
const app = new Koa();
cosnt staticServer = require('koa-static');
app.use(staticServer(root, opts));
  • root 代表你要发送给客户端的静态文件地址
  • opts 代表你要传入的参数选项是对象类型

    3. opts 的具体参数

  • maxage浏览器缓存max-age(以毫秒为单位)。默认为0
  • hidden允许传输隐藏文件。默认为false
  • index 默认文件名,默认为’index.html’
  • defer如果为true,则使用后return next(),允许任何下游中间件首先响应。
  • gzip 当客户端支持gzip时,如果存在扩展名为.gz的请求文件,请尝试自动提供文件的gzip压缩版本。默认为true。
  • br 当客户端支持brotli时,如果存在扩展名为.br的请求文件,请尝试自动提供文件的brotli版本(注意,brotli仅通过https接受)。默认为true。
  • setHeaders用于在响应时设置自定义标头的功能。
  • extensions尝试匹配传递数组中的扩展名以在URL中没有扩展名时搜索文件。首先发现是服务。(默认为false)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const serve = require('koa-static');
const Koa = require('koa');
const app = new Koa();

// $ GET /package.json
app.use(serve('.'));

// $ GET /hello.txt
app.use(serve('test/fixtures'));

// or use absolute paths
app.use(serve(__dirname + 'image.jpg'));

app.listen(3000);

console.log('listening on port 3000');

cookie和session

描述

koa2提供了从上下文直接读取、写入cookie的方法

  • ctx.cookies.get(name, [options]) 读取上下文请求中的cookie
  • ctx.cookies.set(name, value, [options]) 在上下文中写入cookie

注:koa2 没有直接提供操作session的方法,但是我们可以自己设计,如果session数据量比较小的话我们可以直接存在内存中,如果数据量大的话,我们可以存储到数据库中,接下来我简单介绍一下coolie的操作方法。

使用方法

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 === '/index' ) {
ctx.cookies.set(
'cid',
'hello world',
{
domain: 'localhost', // 写cookie所在的域名
path: '/index', // 写cookie所在的路径
maxAge: 10 * 60 * 1000, // cookie有效时长
expires: new Date('2017-02-15'), // cookie失效时间
httpOnly: false, // 是否只用于http请求中获取
overwrite: false // 是否允许重写
}
)
ctx.body = 'cookie is ok'
} else {
ctx.body = 'hello world'
}

})

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