Alex Liang

Koa--使用koa-joi-router驗證API參數及其測試

最近公司新的專案想用koa2來實作API服務,除了一般的post data外還會有第三方傳來的multipart-post data。我們使用koa-joi-router這個套件驗證傳進來的資料,同時使用supertest做測試。

首先說明koa-joi-router跟其它套件有什麼不同,它使用joi這個物件描述語言定義javascript object的schema,我們能限制輸入參數的型態、字元個數及是不是必要欄位。
joi這個套件也能用在一般的javascript object

由於koa-joi-router己內建co-body及await-busboy這二套body-parser,我們不需要再另外裝body-parser。

以下是一個post API的例子:

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 router = require('koa-joi-router');
const Joi = router.Joi;

const public = router();

public.route({
method: 'post',
path: '/comments',
validate: {
type: 'json',
body: {
title: Joi.string().max(100).required,
content: Joi.string().required
}
},
handler: async (ctx) => {
// create article
}
});

const app = new koa();
app.use(public.middleware());
app.listen(3000);

我們必須指定傳進來的資料類型及body要檢查的參數,koa-joi-router有三種type可以使用:json, form和multipart。要注意的是,如果傳進來資料型態和宣告的不同,會直接return 400. 要檢查的參數如果條件不符也會直接回400

而測試程式碼如下(使用mocha和supertest):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const superagent = require('supertest');

const app = require('src/app');

function request() {
return superagent(app.listen());
}

describe('API Test /1/comments', () => {
it('Create a new comment', (done) => {
const testData = {
title: 'Test',
content: 'This is a test comment'
};

request()
.post('/1/comments')
.send(testData)
.expect(200)
.end(done);
});
});

假如API傳進來的資料為form data,則validate的type設定要改為form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public.route({
method: 'post',
path: '/blog',
validate: {
type: 'form',
body: {
title: Joi.string().max(100).required,
content: Joi.string().required
}
},
handler: async (ctx) => {
// create blog post
}
});

測試如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe('API Test /1/blog', () => {
it('Create a new blog post', (done) => {
const testData = {
title: 'Blog Test',
content: 'This is a test post'
};

request()
.post('/1/blog')
.type('form')
.send(testData)
.expect(200)
.end(done);
});
});

最後則是multipart的資料,此種資料一般為上傳檔案或是stream會用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public.route({
method: 'post',
path: '/files',
validate: {
type: 'multipart',
},
handler: async (ctx) => {
const parts = ctx.request.parts;

let part;

try {
while ((part = await parts)) {
console.log(`Receive stream key: ${part[0]}, value: ${part[1]});
}
} catch (err) {
throw new Error(err);
}

const fileContent = parts.field.File;
// upload file
...
}
});

Test code:

1
2
3
4
5
6
7
8
9
10
11
12
13
describe('API Test /1/files', () => {
it('Upload a file', (done) => {
const testData = {
File: 'file content'
};

request()
.post('/1/blog')
.field('File', testData.File)
.expect(200)
.end(done);
});
});

如果本地端使用postman測試的話,這篇文章的答案對於設定會有幫助。

參考資料:
koa-joi-router github
supertest github