使用koa2+es6/7打造高质量Restful API
前言
如今nodejs变得越来越火热,采用nodejs实现前后端分离架构已被多数大公司所采用。
在过去,使用nodejs大家首先想到的是TJ大神写的express.js,而发展到如今,更轻量,性能更好的koa已然成为主流,
它同样出自TJ大神手笔,如今版本已更新到了koa2,不仅性能优异,它还支持async/await,堪称回调地狱的终结者
下面,我们来探讨下,如何使用koa2+es6/7来打造高质量的Restful风格API。
刨根问底,篇幅略长,精华在后面,需要耐心看。
1. 两种模式
一种是耦合模式,即接口层和逻辑层都由一个函数来处理完成。
另一种是分离模式,即接口层和逻辑层是分开的。
下面我们先来说第一种。
耦合模式
先举个粟子,以express为例:
# /server/user/login.js 用户登录
const express = require('express');
const router = express.Router();
router.post('/api/user/login',function(req,res){
// 逻辑层
})
# /server/user/register.js 用户注册
const express = require('express');
const router = express.Router();
router.post('/api/user/register',function(req,res){
// 逻辑层
})
# /server/user/put.js 更改用户资料
const express = require('express');
const router = express.Router();
router.post('/api/user/put',function(req,res){
// 逻辑层
})
这种在过去很常见,相信很多人都写过,我也不例外。但并不推荐。
首先,一个应用的api通常会很多,如果应用够复杂,那意味着你的api可能要处理非常多的逻辑。
而为了应付这些需求,你不得不建立很多的文件,甚至困扰于如何划分和组织好这些文件。
其次,后期并不好维护,当api过多,过于繁杂时,文件深层嵌套,也许你找一个api文件都费神费力。
分离模式
同样先来个粟子:
# /server/router.js
const express = require('express');
const router = express.Router();
router.post('/api/user/login',require('../controllers/users/login')) // 用户登录
.post('/api/user/register',require('../controllers/users/register')) // 用户注册
.put('/api/user/put',require('../controllers/users/put') // 更改用户资料
.delete('/api/user/deluser',require('../controllers/users/deluser')) // 删除用户
……
很显然,这种api已将接口层和逻辑层分离了,接口层由一个router.js文件来统一定义,而每个接口的逻辑层则由单独的文件来处理,并按不同功能模块用不同文件夹来组织这些逻辑文件。
那么,这样做有什么好处呢?
首先,很直观,整个结构很清晰,一目了然
其次,只需要你专注于处理逻辑
再者,api集中在router.js文件定义,同事更容易看懂你的代码结构,或者知道你增改了哪些api等等,这很方便于多人协同开发,在大型开发中尤为重要
很显然,分离模式优于耦合模式。
2. 如何更好地组织逻辑层
经过上面的分析之后,我们选择更优的分离模式, 它只需要你关注逻辑层。
但是,以上面分离模式的例子为例,每一个接口仍然需要单独一个js文件来处理它的逻辑层,并且需要用很多不同文件夹来组织它
们,假如应用足够大,有几十甚至上百个api,那意味着很有可能你的js逻辑文件也达几十乃至上百个,而用来划分和组织这些js文
件的文件夹也不在少数。
这就造成了过于臃肿,难以维护的毛病。
那么,有没有可能,一个功能模块只需要一个js文件来处理它们的所有逻辑层,并更具可维护性呢?
打个比方,现在有一个博客站点,我仅使用一个user.js文件来处理用户模块所有api的逻辑层,包括注册,登录,修改,删除,密码重置等等,另外用一个article.js文件来处理文章模块所有api的逻辑层,包括发布,修改,获取详情,点赞,评论,删除等等。
如果可以做到这样,那就意味着代码量大大减少,且可维护性更高。
而要做到这步,我们需要解决两个问题,一个是异步回调,因为异步回调使我们增加了很多代码量,逻辑复杂,二是如何批量定义和导出大量api的逻辑层方法。
首先,我们先来解决异步回调这个问题,下面将会展开讲解。
为了减少篇幅,下面只做简要的浅析。
express 时代
我们先来回顾一下历史。
鉴于nodejs的回调机制,很多异步操作都需要回调来完成,如果你的逻辑足够复杂,很可能就会陷进回调地狱,下面是一个简单的例子:
……
fs.readFile('/etc/password', function(err, data){
// do something
fs.readFile('xxxx', function(err, data){
//do something
fs.readFile('xxxxx', function(err, data){
// do something
})
})
})
……
同样,express也不例外,常常会让你深陷回调地狱。通常一个api需要写大量的代码来完成,此时为了更好地开发和维护,你不得不每个api都单独一个js文件来处理。
为了解决异步回调这个大问题,js生态出现了很多解决方案,
其中比较好的两个——promise,async。
promise, async时代
首先说说async。
这曾是一个非常优秀的第三方模块,它基于回调机制来实现,是处理异步回调很好的解决方案,如今github上已超两万多颗星。
async提供两个非常好的处理异步的方法,分别是串行执行的waterfall,以及并行执行的parallel。
下面来个粟子:
# waterfall 按顺序执行,执行完一个,传递给下一个,最终结果返回给最后的回调函数
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
console.log(result);
});
# parallel 并行执行,即同时执行
async.parallel([
function(callback){
callback(null, 'one');
},
function(callback){
callback(null, 'two');
}
],
function(err, results){
// 最终处理
});
很显然,这很大程度上避免了回调地狱,并且有一个完整的控制流,使你可以很好的组织代码。
接下来说说promise
作为一名合格的前端,你有必要对promise有所了解,可以参考阮一峰写的es6入门之promise。
首先,promise是es6的特性之一,实际是可用来传递异步操作流的对象。
promise提供三种状态,Pending(进行中),Resolved(已解决),Rejected(已失败)。
promise提供两个方法,resolve()和reject(),可用于处理最终结果。
promise还提供一个then方法,用于异步处理过程,这是一个控制流方法,可以不停地执行下去,直到得到你想要的结果。
promise还提供了catch方法,用于捕获和处理异步处理过程中出现的异常。
下面来举个粟子:
var promise = new Promise(function(resolve, reject) {
// 一些异步逻辑,比如ajax, setTimeout等
if (/* 异步操作成功 */){
resolve(value); // 成功则返回结果
} else {
reject(error); // 失败则返回错误
}
}).then(function(value){
// 不是想要的结果,继续往下执行
}).then(function(value){
// 不是想要的结果,继续往下执行
}).then
……
}).then(function(value){
// 是最终想要的结果
}).catch(function(err){
throw err; // 如果有异常则抛出
})
那么,能不能同时执行多个promise实例呢?
可以的,promise.all()方法可以帮到你。
不得不说,promise是解决异步回调的一大进步,是一个非常优秀的解决方案。而由于promise的强大,生态圈出现了很多基于promise的优秀模块, 比如bluebird, q等等。
然而,promise并非终点,它只是弱化了回调地狱,并不能真正消除回调。使用promise仍然要处理很多复杂的逻辑,以及写很多的逻辑代码
而要消除回调,意味着要实现以同步的方式来写异步编程。
那么如何来实现?
此时舞台再次交给TJ大神,因为他写了个co,利用generator协程机制,实现以同步的方式来写异步编程。
不得不膜拜下TJ大神。
generator 时代
关于generator的相关知识,可参考阮一峰老师写的es6入门之generator。
和promise一样,generator同样是es6的新特性,但它并非为解决回调而存在的,只是它恰好拥有这个能力,而TJ大神看到这种可能,于是他利用generator封装了co。并基于co,他又创造了个更轻量,性能更好的koa1web框架。
自此,koa1终于诞生了!它迎合了es6和co,
koa1和express相比,有非常大的进步,其中之一就是它很大程度上真正地解决了异步回调问题,真正意义上实现同步方式来写异步编程。
再就是,koa1更轻量,性能比express更为优异。
koa1实现同步写异步的关键点就是co。那么,co是如何实现同步写异步的呢?
下面继续来个举个粟子:
# 正常的异步回调
var request = require('request');
var a = {};
var b = {};
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
a.response = response;
a.body = body;
request('http://www.yahoo.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
b.response = response;
b.body = body;
}
});
}
});
# co异步处理
co(function *(){
var a = yield request('http://google.com'); // 以同步的方式,直接拿到异步结果,并往下执行
var b = yield request('http://yahoo.com');
console.log(a[0].statusCode);
console.log(b[0].statusCode);
})()
看完这个粟子,是不是十分的激动呢?
我们再来看看,基于co的koa1是如何处理异步的, 同样举个粟子:
# 发布文章接口
const parse = require('co-body');
const mongoose = require('mongoose');
const Post = mongoose.model('Post');
// 发布文章
exports.create = function *() { // 使用 *表示这是一个gentator函数
let post = new Post(this.req.body.post);
let tags;
post.set('user_id', this.user.id);
if (yield post.save()) { // yield直接获取异步执行的结果
this.redirect('/admin/posts');
} else {
tags = yield Tag.findAll();
this.body = yield render('post/new', { post: post.toJSON(), tags: tags.toJSON() }); // yield直接获取异步执行的结果
}
}
想象一下,这个例子如果使用express来做会是怎样呢?
相信你心中有数,很无情地抛弃了express,express哭晕厕所
使用koa2+es6/7打造高质量Restful API的更多相关文章
- 打造高质量Android应用:Android开发必知的50个诀窍
打造高质量Android应用:Android开发必知的50个诀窍
- 4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍
4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍 天涯明月笙 关注 2018.02.20 19:23* 字数 762 阅读 135 ...
- 阿里云推出SRT+杜比全景声直播方案,低成本打造高质量直播观感体验
超过200个国家和地区共5144万人观看:浙江卫视.东方卫视55城总收视达2.39,稳居同时段市场第一:优酷直播间63%观看晚会的用户参与了互动:微博68.2亿的主话题阅读量:2019天猫双11狂欢夜 ...
- Python Tornado搭建高并发Restful API接口服务
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快能实现高并发.得利于其 非阻塞的方式和对epoll的运用,Torn ...
- 设计一个高质量的API接口
参考网址:http://url.cn/5UaTeyv 前言 在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性.接口的稳定性.接口的跨域性.接口的协议规则.接口的路径规则. ...
- 利用koa打造restful API
概述 最近学习利用koa搭建API接口,小有所得,现在记录下来,供以后开发时参考,相信对其他人也有用. 就目前我所知道的而言,API有2种,一种是jsonp这种API,前端通过ajax来进行跨域请求获 ...
- 第二章 设计高质量的React组件
第二章 设计高质量的React组件 高质量React组件的原则和方法: 划分组件边界的原则: React组件的数据种类: React组件的生命周期. 2.1 易于维护组件的设计要素 1.高内聚:指的是 ...
- 如何编写高质量的 JS 函数(2) -- 命名/注释/鲁棒篇
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/sd2oX0Z_cMY8_GvFg8pO4Q作者:杨昆 上篇<如何编写高质量的 JS 函数 ...
- 从0使用Ruby on Rails打造企业级RESTful API项目实战之我的云音乐
本节对我们项目实现的功能和知识点做一个简单的介绍,因为是RESTful API项目,所以对于后端来说基本上没有什么UI界面可展示,那我们就在关键的点,使用客户端(Android)实现的效果图. 课程简 ...
随机推荐
- 使用Chrome浏览器访问谷歌和维基百科
安装chrome插件 谷歌访问助手即可 就这么简单
- IIS的UrlRewrite模块
以前在webform中重写URL是在Global.asax中的Addplication_BeginRequest事件中写代码进行跳转 今天介绍使用IIS提供的UrlRewrite模块实现URL重写 首 ...
- day48 前端高级选择器优先级
复习 1. 基础选择器 标签选择器(div) | 类选择器(.div1) | id选择器(#div2) <div class="div1" id="div2&quo ...
- php+javascript实现的动态显示服务器运行程序进度条功能示例
本文实例讲述了php+javascript实现的动态显示服务器运行程序进度条功能.分享给大家供大家参考,具体如下: 经常有这样的业务要处理,服务器上有较多的业务需要处理,需要分批操作,于是就需要一个提 ...
- npm 离线安装依赖
现实场景:一台自己的电脑可以连外网,一台开发机不能连网,开发机需要安装node_modules 依赖解决办法: npm 安装依赖分为两种,一是 -g 这种是安装在全局环境的,只有在电脑中 ...
- 第一次博客作业 <西北师范大学| 周安伟>
1.助教博客链接:https://home.cnblogs.com/u/zaw-315/ 2.本周点评的作业数:3份,有留言互动. 3.本周点评有困难的地方: https://www.cnblogs ...
- 混合现实开发教程unity2017
共52节,MP4格式,英字,大小1GB 扫码时备注或说明中留下邮箱 付款后如未回复请至https://shop135452397.taobao.com/ 联系店主
- 虚拟网络学习笔记之一:VXLAN
1. 什么是虚拟网络? 答:从架构角度考虑,我们可以采用与服务器虚拟化引入Hypervisor的方式一样,引入Nypervisor或者叫“虚拟网络管理平台”实现虚拟网络.虚拟网络必须像虚拟机一样,脱离 ...
- UEFI EVENT 全解
Event和Timer在UEFI当中是怎么实现的以及原理,我们先从Timer开始,然后细细的拨开隐藏在底层的实现. 先说Timer,那什么是Timer呢?其实在中文里面我们把它叫做定时/计数器,但是我 ...
- docker-compose使用
1.创建app.py项目文件,执行以下代码 import time import redis from flask import Flask app = Flask(__name__) cache = ...