本篇主要介绍如何在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
1234 [{"name":"xiaoming","age":20},{"name":"hang","age":"26"}]2、路由文件编写,为了优化代码结构,将路由代码写在一个单独的文件 router.js 中
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 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 //状态 500ctx.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.agetry {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 = 500ctx.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 = agebreak}}yield writeToFile(JSON.stringify(users))ctx.body = JSON.stringify({status: 'success'})} catch(err) {logger.error('err', err)ctx.status = 500ctx.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 = 500ctx.body = JSON.stringify({status: 'failed'})}}))module.exports = route //导出模块3、 app.js文件修改
在 app.js 中加入 koa-bodyparser, 抽离 router相关代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445 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文件
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 <body><div class="get"><h2>Get all users</h2><div id='users'><p>Name: -- 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, deletecredentials: '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 + ' 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').valueuser.age = document.getElementById('add-age').valuefetchOprate('/api/user', 'POST', user) //发生请求})//patch 请求document.getElementById('mod-user').addEventListener('click', function() {var name = document.getElementById('mod-name').valuevar age = document.getElementById('mod-age').valuefetchOprate('/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').valuefetchOprate('/api/user/'+name, 'DELETE', {})})})()</script>
五、测试
1、启动服务, 在浏览器输入 http://localhost:3000/, 如下图所示:
2、 获取数据: 单击 “GET User”, 如下图所示: 在json中加入的数据都加载出来了
3、 增加数据: 输入name, age, 点击 Add User, 再点击 GET User: 可以看到新增的数据
4、 修改数据,和删除数据 操作类似
5、 通过查看user.json文件中的数据也能判断操作是否成功