第四篇:koa2-web应用进阶—REST接口

本篇主要介绍如何在koa web应用上编写 RESTful 风格的api接口

一、REST 方法

GET - 用于获取方法
POST - 添加或更新数据
PATCH - 更新数据
DELETE - 删除数据

二、依赖引入

1、REST 接口的实现主要是依靠 koa-router 来实现 koa-router
2、 koa-bodyparser: 解析 http请求body的中间件 安装: npm install koa-bodyparser@next –save (@next 表支持 koa2 的版本 )

三、实现

实现方案: 使用一个json文件作为数据资源文件(代替数据库), 实现数据的增、删、改、查功能
1、 新建一个json文件, user.json

1
2
3
4
[
{"name":"xiaoming","age":20},
{"name":"hang","age":"26"}
]

2、路由文件编写,为了优化代码结构,将路由代码写在一个单独的文件 router.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const co = require('co')
const fs = require('fs')
const router = require('koa-router')
const log4js = require('koa-log4')
const route = new router()
const logger = log4js.getLogger('router')
let filePath = './user.json' //json文件路径
let readFromFile = () => { //读文件函数
return new Promise((resolve, reject) => { //封装成Promise对象
fs.readFile(filePath, (err, data) => {
if(err) {
reject(err)
}
resolve(data)
})
})
}
let writeToFile = (content) => { //写入文件
return new Promise((resolve, reject) => {
fs.writeFile(filePath, content, err => {
if(err) {
reject(err)
}
resolve({state: 'ok'})
})
})
}
route.get('/', co.wrap(function* (ctx) { //访问根目录
logger.debug(' this is test log')
if(ctx.session.view === undefined) {
ctx.session.view = 0
} else {
ctx.session.view += 1
}
console.log('viewNum', ctx.session.view)
yield ctx.render('index', {title: 'Nunjucks'}) //渲染模板views/index.html, 后面RESTful接口使用要用到该html文件
}))
// API GET, 查询数据
route.get('/api/user', co.wrap(function* (ctx, next) {
try {
let users = JSON.parse(yield readFromFile()) //读取所有数据
ctx.body = JSON.stringify({status: 'success', data: users}) //返回json格式数据
} catch(err) { //异常
logger.error('err', err)
ctx.status = 500 //状态 500
ctx.body = JSON.stringify({status: 'failed'}) //返回错误状态
}
}))
//API POST, 添加数据
route.post('/api/user', co.wrap(function* (ctx, next) {
let newUser = {}
//能够 通过request.body这种方式获取数据,归功于 koa-bodyparser 这个中间件
newUser.name = ctx.request.body.name //获取post请求参数
newUser.age = ctx.request.body.age
try {
let users = JSON.parse(yield readFromFile()) //读取所有数据
users.push(newUser) //加入新数据
yield writeToFile(JSON.stringify(users)) //重新写回json文件
ctx.body = JSON.stringify({status: 'success'}) //请求成功
} catch(err) {
logger.error('err', err)
ctx.status = 500
ctx.body = JSON.stringify({status: 'failed'})
}
}))
//API PATCH, 修改数据
route.patch('/api/user/:name', co.wrap(function* (ctx, next) {
let name = ctx.params.name //获取url中携带的参数
let age = ctx.request.body.age //获取请求body中的参数
try {
let users = JSON.parse(yield readFromFile())
for(let i=0; i<users.length; i++) { //更新数据
if(users[i].name === name) {
users[i].age = age
break
}
}
yield writeToFile(JSON.stringify(users))
ctx.body = JSON.stringify({status: 'success'})
} catch(err) {
logger.error('err', err)
ctx.status = 500
ctx.body = JSON.stringify({status: 'failed'})
}
}))
//API DELETE, 删除数据
route.del('/api/user/:name', co.wrap(function* (ctx, next) {
let name = ctx.params.name //获取url参数
try {
let users = JSON.parse(yield readFromFile())
for(let i=0; i<users.length; i++) { //删除name所指数据
if(users[i].name === name) {
users.splice(i, 1)
break
}
}
yield writeToFile(JSON.stringify(users))
ctx.body = JSON.stringify({status: 'success'})
} catch(err) {
logger.error('err', err)
ctx.status = 500
ctx.body = JSON.stringify({status: 'failed'})
}
}))
module.exports = route //导出模块

3、 app.js文件修改
在 app.js 中加入 koa-bodyparser, 抽离 router相关代码

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
const Koa = require('koa')
const co = require('co')
const nunjucksViews = require('koa-nunjucks-promise')
const mount = require('koa-mount')
const server = require('koa-static')
const session = require("koa-session2")
//--------------------
const bodyParser = require('koa-bodyparser') // 引入koa-bodyparser
//--------------------
const log4js = require('koa-log4')
require('./log')
const logger = log4js.getLogger('app')
//-----------------
const route = require('./router') //引入 router 配置
//-----------------
logger.info('--------step into koa-------------')
const app = new Koa()
app.use(nunjucksViews(`${__dirname}/views`, {
ext: 'html',
noCache: true,
watch: true,
filters: { //过滤器
json: function(str) {
return JSON.stringify(str, null, 2)
}
},
globals: { //设置对于nunjucks的全局变量
// staticPath: '//static'
}
}))
app.use(mount('/static', server(`${__dirname}/public`)))
app.use(log4js.koaLogger(log4js.getLogger('http'), { level: 'auto' }))
//-----------------
app.use(bodyParser()) //使用 bodyparse 解析参数 默认支持json, form
//------------------
app.use(session({
key: "sessionId", //default "koa:sess",
maxAge: 5000
}))
//---------------
app.use(route.routes()) //路由
.use(route.allowedMethods())
//----------------
app.listen(3000, () => console.log('server started, port 3000'))
module.exports = app

四、RESTful API 调用

方案: 在index.html文件中,通过 fetch api 上述的4个接口,实现数据的增、删、改、查
1、 修改index.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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<body>
<div class="get">
<h2>Get all users</h2>
<div id='users'>
<p>Name: -- &nbsp;&nbsp;age: --</p>
</div>
<input type="button" id="get-users", value="GET User">
</div>
<div class="post">
<h2>Add users</h2>
<lable>Name:</lable> <input type="text" id="add-name" placeholder="input name" > <br>
<lable>Age:</lable> <input type="text" id="add-age" placeholder="input age" > <br>
<input type="button" value="Add user" id="add-user">
</div>
<div class="update">
<h2>Modify users</h2>
<lable>Name:</lable> <input type="text" id="mod-name" placeholder="input name" > <br>
<lable>Age:</lable> <input type="text" id="mod-age" placeholder="input age" > <br>
<input type="button" value="Mod user" id="mod-user">
</div>
<div class="delete">
<h2>Delete users</h2>
<lable>Name:</lable> <input type="text" id="del-name" placeholder="input name" > <br>
<input type="button" value="Del user" id="del-user">
</div>
</body>
<script>
(function() {
var fetchOprate = (url, method, data) => {
fetch(url, {
method: method, //请求方法 post, patch, delete
credentials: 'include',
headers: { //带上这个请求头,否则request body无法解析
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}).then(res => {
res.json().then(resp => { //返回JOSN字符串解析
console.log('resp', resp) //console输出字符串
})
}).catch(err => {
console.log('err', err)
})
}
//get 请求
document.getElementById('get-users').addEventListener('click', function() {
fetch('/api/user', {
method: 'GET',
credentials: 'include',
}).then(res => {
res.json().then(resp => {
var elem = ''
for(var i=0; i<resp.data.length; i++) { //将渲染结果
var item = resp.data[i]
elem += '<p>Name: ' + item.name + '&nbsp;&nbsp;age: ' + item.age + '</p>'
}
console.log('elem', elem)
document.getElementById('users').innerHTML = elem
})
}).catch(err => {
console.log('err', err)
})
})
//post 请求
document.getElementById('add-user').addEventListener('click', function() {
var user = {}
user.name = document.getElementById('add-name').value
user.age = document.getElementById('add-age').value
fetchOprate('/api/user', 'POST', user) //发生请求
})
//patch 请求
document.getElementById('mod-user').addEventListener('click', function() {
var name = document.getElementById('mod-name').value
var age = document.getElementById('mod-age').value
fetchOprate('/api/user/'+name, 'PATCH', {age: age}) //发送请求
//在router.js文件中,该接口的请求路径为 '/api/user/:name', 需带上参数 name
})
//delete 请求
document.getElementById('del-user').addEventListener('click', function() {
var name = document.getElementById('del-name').value
fetchOprate('/api/user/'+name, 'DELETE', {})
})
})()
</script>

五、测试

1、启动服务, 在浏览器输入 http://localhost:3000/, 如下图所示:
restful
2、 获取数据: 单击 “GET User”, 如下图所示: 在json中加入的数据都加载出来了
restful
3、 增加数据: 输入name, age, 点击 Add User, 再点击 GET User: 可以看到新增的数据
restful
4、 修改数据,和删除数据 操作类似
5、 通过查看user.json文件中的数据也能判断操作是否成功