菜单

js_脚本之家,koa2从运营到填坑

2020年4月4日 - 澳门太阳娱乐手机登录
js_脚本之家,koa2从运营到填坑

协作上一篇小说的沟通人接受(

原稿链接:

1. 所需工具

前传

node.js mongoDB

是因为兴趣前段时间初始研讨koa2,由于事情未发生前有过一些express涉世,以为koa依然很好上手的,可是用起来开掘还是多少地方轻易懵逼,因而收拾此文,希望能够支持到一些新妇。

2. 主要node模块

举个例子您不懂javascript,建议您先去撸三次红宝书javascript高等程序设计

koa()

只要您不纯熟ES6,提议您先去撸叁回阮一峰先生的ECMAScript
6入门

3. 目录布局

因为自个儿也是新妇,笔者只是整理了自己的就学经历,怎么着填平踩到的坑。

4. 启动MongoDB

假设有读者开掘小编有写错的地点希望您能立即留言给自己,别让本人误导了别的新手。

率先在MongoDB安装盘的根目录下新建二个文本夹data,然后在MongoDB的bin中开拓终端,输入mongod
–DBpath d:data,那样MongoDB的数量存放地方就构造好了。

正文的体系景况Mac OS

然后双击bin中的mongo.exe,mongoDB就开动完毕了。

编译器 VScode

5. app.js

1 创设项目

app.js为进口文件,成效是接二连三数据库,导入文本,引进koa组件,最终运转服务。

想利用koa,大家必定将首先想到去官方网站看看,没准有个guide之类的能够轻易入门,不过koa官网跟koa自己同样轻易。

'use strict';const fs = require;const path = require;const mongoose = require;const db = 'mongodb://localhost/test';/* 连接数据库 */mongoose.Promise = require;mongoose.connect(db, {useMongoClient: true});/** * 获取数据库表对应的js对象所在的路径 * @type {[type]} */const models_path = path.join(__dirname, '/app/models');/** * 已递归的形式,读取models文件夹下的js模型文件,并require * @param {[type]} modelPath [description] * @return {[type]} [description] */let walk = function  { fs.readdirSync.forEach { let filePath = path.join(modelPath, '/' + file) let stat = fs.statSync if  { if /.test { require } } else if  { walk};walk;require;const Koa = require;const logger = require;const session = require;const bodyParser = require;const app = new Koa;app.use;app.use;/** * 使用路由转发请求 * @type {[type]} */const router = require;app.use;app.use(router.allowedMethods;console.log('app started at port 3000...');

要是要自身一丝丝搭建景况的话,以为好辛苦,所以先去找了找有未有品种生成器,然后就意识了狼叔-桑世龙写的koa-generator。

6. 路由布置

1.1 安装koa-generator

路由配置在config/router.js中进行。

在终点输入:

const Router = require;const User = require('../app/controllers/user');module.exports = function () { let router = new Router; router.post('/test/user/users', User.users); router.post('/test/user/user', User.user); router.post('/test/user/add', User.addUser); router.post('/test/user/delete', User.deleteUser); return router};

$ npm install -g koa-generator

post方法第一参数为路由地址,第二参数为路由地址对应的不二等秘书籍。

1.2 使用koa-generator生成koa2项目

7. 表布局定义

在你的劳作目录下,输入:

表布局定义在app/models/user.js中。

$ koa2 HelloKoa2

let mongoose = require;let Schema = mongoose.Schema;// 定义表结构let UserSchema = new Schema({ name: { type: String, required: true }, sex: String, area: String, always: Boolean, relationship: Array, mobile: String, phone: String, desc: String, id: String});// 参数User 数据库中的集合名称, 不存在会创建.let User = mongoose.model;module.exports = User;

瓜熟蒂落创立项目后,步入项目目录,并奉行npm install命令

8. 工具方法

$ cd HelloKoa2

有些增加和删除改查的工具方法放在app/dbhelp/userHelp.js中

$ npm install

'use strict';let mongoose = require;let User = mongoose.model;/* 查找用户 */exports.findAllUsers = async () => { let query = User.find(); let res = []; await query.exec { if  { res = []; } else { res = users; } }); return res};/* 查找特定用户 */exports.findFilterUsers = async  => { let nameReg = new RegExp; let query = User.find({ name: { $regex: nameReg } }); let res = []; await query.exec { if  { res = [] } else { res = users; } }); return res};/* 查找单个用户 */exports.findUser = async  => { let query = User.find; let res = {}; await query.exec { if  { res = '没有该用户'; } else { res = tUser[0]; } }); return res};/* 新增用户 */exports.addUser = async  => { user = await user.save(); return user};/* 编辑用户 */exports.updateUser = async  => { user = await User.update({id: user.id}, { $set: { name: user.name, sex: user.sex, area: user.area, always: user.always, relationship: user.relationship, phone: user.phone, mobile: user.mobile, desc: user.desc } }); return user};/* 删除用户 */exports.deleteUser = async  => { let flag = false; console.log('flag==========>' + flag); await User.remove { if  { flag = false } else { flag = true } }); console.log('flag=====await=====>' + flag); return flag};

1.3 运行项目

9. 路由布置中对应的措施

在极限输入:

路由配置中对应的方法在app/controller/user.js中。

$ npm start

'use strict';let xss = require;let mongoose = require;let User = mongoose.model;let uuid = require;import userHelper from '../dbhelper/userHelper.js'/* 多用户 */exports.users = async  => { let data; if  { data = await userHelper.findFilterUsers } else { data = await userHelper.findAllUsers() } ctx.body = { success: true, data }};/* 单用户 */exports.user = async  => { let data = await userHelper.findUser ctx.body = { success: true, data }};/* 添加用户 */exports.addUser = async  => { let newObj = ctx.request.body, user2; let id = newObj.id || uuid.v4(); let user = new User({ name: newObj.name, sex: newObj.sex, area: newObj.area, always: newObj.always, relationship: newObj.relationship, phone: newObj.phone, mobile: newObj.mobile, desc: newObj.desc, id: id }); if  { user2 = await userHelper.updateUser; } else { user2 = await userHelper.addUser { ctx.body = { success: true, data: user2 } }};/* 删除用户 */exports.deleteUser = async  => { let id = xss; let data = await userHelper.deleteUser; ctx.body = { success: true, data }};

类型运营后,默许端口号是3000,在浏览器中运作能够获得下图的效率表达运维成功。

总结:

图片 1

骨子里远非什么样花样,无非都以api的采纳,这里超级多用async与await达成异步操作,阮先生的篇章里有async的万事,

在那再度多谢狼叔-桑世龙

如上便是本文的全体内容,希望对我们的求学抱有利于,也期待我们多都赐教脚本之家。

脚下项指标文件目录如下图

图片 2

FileTree

1.4 关于koa2

1.4.1 中间件的实行各种

koa的中间件是由generator组成的,那决定了中间件的实施顺序。

Express的中间件是各种推行,从第壹当中间件实践到结尾三个中间件,发出响应。

图片 3

koa是从第六此中间件初步实践,境遇next步向下壹此中间件,平昔实施到最终六当中间件,在逆序,推行上二个中间件next之后的代码,一贯到第叁个中间件实施实现才发出响应。

图片 4

1.4.2 async await语法帮衬

koa2充实了asyncawait语法的扶助.

原先koa的中间件写法

app.use(function*(next){varstart =newDate;yieldnext;varms =newDate-
start;this.set(‘X-Response-Time’, ms +’ms’);});

koa第22中学的写法

app.use(async(next) => {varstart =newDate;awaitnext();varms =newDate-
start;this.set(‘X-Response-Time’, ms +’ms’);});

koa注解说要在v3版本中撤销对generator中间件的帮忙,所以为了长时间思忖仍旧用async语法的好。

若是想要继续采取function*语法,能够使用koa-convert那当中间件实行转移。那也是您见到项目中会有上面代码的因由

constconvert
=require(‘koa-convert’);app.use(convert(bodyparser));app.use(convert(json()));app.use(convert(logger()));

1.4.3 Context

Context封装了node中的request和response。

koa@1.x使用this引用Context对象:

app.use(function*(){this.body =’Hello World’;});

koa@2.x中使用ctx来访问Context对象:

app.use(async(ctx, next) => {awaitnext();  ctx.body =’Hello
World’;});

地方代码中的ctx.body = ‘Hello
World’那行代码表示设置response.body的值为’Hello World’。

例如你看文书档案就有希望懵逼,那么作者发送post伸手的参数应该怎么获取呢?

通常ctx不可能平素拿走request的body,想要获取post恳请中的参数要动用ctx.request.body。

如需查看项目代码 –> 代码地址:

https://github.com/tough1985/hello-koa2

选择Tag -> step1

2 项目计划

这里的安顿指的是运转情状的结构,比方大家在开荒阶段使用当地的数据库,测量检验要采纳测量检验库,公布上线时候使用线上的库,也可能有差异的端口号。

2.1 当我们输入npm start的时候都干了些什么

package.json文件中

“scripts”: {

“start”: “./node_modules/.bin/nodemon bin/run”,

“koa”: “./node_modules/.bin/runkoa bin/www”,

“pm2”: “pm2 start bin/run “,

“test”: “echo “Error: no test specified” && exit 1″

}

能够见见这部分内容,当我们在顶峰输入:

$ npm start

在就能运营package.jsonscripts对象对应的start字段前边的剧情,也正是您在顶峰输入:

$ ./node_modules/.bin/nodemon bin/run

nodemon插件的功用是在你运行了劳务之后,改过文件能够自动重启服务。

关于nodemon的更加多内容
–>nodemon

若是不考虑自动重启功用,其实这句代码也便是实行了node bin/run

大家能够见见项目标bin目录下,有一个run文件,代码如下:

#!/usr/bin/env node

var current_path = process.cwd();

require(‘runkoa’)(current_path + ‘/bin/www’ )

那边引进了三个runkoa,那一个组件是狼叔写的koa2对babel情形注重的三个卷入插件。

关于runkoa相关内容表达–>runkoa。这里大家最后会履行bin目录下的www文件来运营服务。

2.2 npm scripts

我们在scripts对象中增加一段代码”start_koa”:
“bin/run”,修改后scripts对象的源委如下:

“scripts”: {

“start”: “./node_modules/.bin/nodemon bin/run”,

“koa”: “./node_modules/.bin/runkoa bin/www”,

“pm2”: “pm2 start bin/run “,

“test”: “echo “Error: no test specified” && exit 1″,

“start_koa”: “bin/run”

}

那么既然输入npm
start实行start背后的台本,聪明的你一定会想:是或不是自身输入npm
start_koa就足以推行start_koa末尾相关的代码了啊?

甭管您是怎么想的,反正自身立即就想的如此天真。

实质上大家输入npm start_koa之后,终端会提醒npm不曾有关的吩咐。

那么在scripts中的js_脚本之家,koa2从运营到填坑。start_koa指令要怎么接受呢,其实要加四个run指令技艺施行,在终点输入:

$ npm run start_koa

能够见见服务平常运作了。

npm中,有三个常用的缩写

npm start是npm run start

npm stop是npm run stop的简写

npm test是npm run test的简写

npm restart是npm run stop && npm run restart && npm run start的简写

别的的都要利用npm run来施行了。

推荐读一遍阮一峰先生写的npm scripts
使用指南,很有帮带。

2.3 配置意况

至于配置情形常用的有development、test、production、debug。

能够运用node提供的process.env.NODE_ENV来设置。

在起步服务的时候能够对NODE_ENV扩充赋值,举例:

$ NODE_ENV=test npm start

然后大家得以在bin/www文本中输出一下,看看是或不是配备成功,增添如下代码:

console.log(“process.env.NODE_ENV=”+ process.env.NODE_ENV);

然后在极端输入

$ NODE_ENV=test npm start

能够看来终端打字与印刷:

process.env.NODE_ENV=test

大家能够在scripts对象少校情况计划好,比方大家将starttest分别设置developmenttest条件,代码如下:

“scripts”: {

“start”: “NODE_ENV=development ./node_modules/.bin/nodemon bin/run”,

“koa”: “./node_modules/.bin/runkoa bin/www”,

“pm2”: “pm2 start bin/run “,

“test”: “NODE_ENV=test echo “Error: no test specified” && exit 1″,

“start_koa”: “bin/run”

},

能够在极限分别输入npm start和npm test来测验情形配置是不是见到成效。

鉴于并不曾测量试验内容,以后的test脚本会退出,后边大家在前述koa的测量试验。

2.4 配置文件

为了能够基于分化的运营条件加载不相同的配备内容,大家须要加上一些安插文件。

率先在项目根目录下加多config目录,在config目录下增添index.js、test.js、development.js四个公文,内容如下。

development.js

/**

* 开拓遭遇的布署内容

*/module.exports =
{env:’development’,//情况名称port:3001,//服务端口号mongodb_url:”,//数据库地址redis_url:”,//redis地址redis_port:”//redis端口号}

test.js

/**

* 测量试验遇到的配备内容

*/module.exports =
{env:’test’,//情状名称port:3002,//服务端口号mongodb_url:”,//数据库地址redis_url:”,//redis地址redis_port:”//redis端口号}

index.js

vardevelopment_env =require(‘./development’);vartest_env
=require(‘./test’卡塔尔国;//依照分裂的NODE_ENV,输出不一致的配备对象,默许输出development的配备对象module.exports
= {development: development_env,test: test_env}[process.env.NODE_ENV
||’development’]

代码应该都没事儿可疏解的,然后大家再来编辑bin/www文件。

bin/www累积如下代码

//引入配置文件varconfig =require(‘../config’卡塔尔(قطر‎;//
将端口号设置为布局文件的端口号,暗中同意值为3000varport =
normalizePort(config.port ||’3000’);// 打字与印刷输出端口号console.log(‘port =
‘+ config.port卡塔尔;

测量试验效果,在终极输入npm start,能够见到

process.env.NODE_ENV=development

port = 3001

到浏览器中访谈http://127.0.0.1:3001,能够见到原本的输入内容,表达配置文件已经生效。

如需查看项目代码 –> 代码地址:

https://github.com/tough1985/hello-koa2

选择Tag -> step2

3 日志

狼叔koa-generator现已增多了koa-logger,在app.js文本你能够找到那样的代码:

constlogger =require(‘koa-logger’);……app.use(convert(logger()));

koa-logger是tj大神写的koa开辟时替换console.log输出的多个插件。

倘让你需求依照时间依旧根据文件大小,当地输出log文件的话,提议依然使用log4js-node。

3.1 log4js

log4js提供了多少个日志品级分类,同期也能替换console.log输出,别的他还足以根据文件大小也许日期来生费用地日志文件,还足以选用邮件等花样发送日志。

作者们在这里演示用infoerror三种日志品级分别记录响应日志和谬误日志。

3.2 log4js 配置

config目录下创办叁个log_config.js文本,内容如下:

varpath =require(‘path’卡塔尔国;//错误日志输出完整路线varerrorLogPath =
path.resolve(__dirname,”../logs/error/error”卡塔尔;//响应日志输出完整路线varresponseLogPath
= path.resolve(__dirname,”../logs/response/response”);module.exports =
{“appenders”:   
[//错误日志{“category”:”errorLogger”,//logger名称”type”:”dateFile”,//日志类型”filename”:
errorLogPath,//日志输出地方”alwaysIncludePattern”:true,//是不是总是有后缀名”pattern”:”-yyyy-MM-dd-hh.log”//后缀,每小时创造二个新的日志文件},//响应日志{“category”:”resLogger”,”type”:”dateFile”,”filename”:
responseLogPath,”alwaysIncludePattern”:true,”pattern”:”-yyyy-MM-dd-hh.log”} 
 
],”levels”://设置logger名称对应的的日志品级{“errorLogger”:”E途达ROSportage”,”resLogger”:”ALL”}}

然后创造一个utils目录,添加log_util.js文本,内容如下:

varlog4js =require(‘log4js’);varlog_config
=require(‘../config/log_config’卡塔尔;//加载配置文件log4js.configure(log_config卡塔尔(قطر‎;varlogUtil
= {};varerrorLogger = log4js.getLogger(‘errorLogger’卡塔尔国;varresLogger =
log4js.getLogger(‘resLogger’State of Qatar;//封装错误日志logUtil.logError
=function(ctx, error, resTime卡塔尔{if(ctx && errorState of Qatar {       
errorLogger.error(formatError(ctx, error, resTime卡塔尔卡塔尔;   
}};//封装响应日志logUtil.logResponse =function(ctx, res提姆e卡塔尔{if(ctx卡塔尔 { 
      resLogger.info(formatRes(ctx, resTime卡塔尔State of Qatar;   
}};//格式化响应日志varformatRes =function(ctx, resTime卡塔尔(قطر‎{varlogText
=newString(卡塔尔;//响应日志初叶logText
+=”n”+”*************** response log start
***************”+”n”;//增加诉求日志logText +=
formatReqLog(ctx.request, res提姆e卡塔尔;//响应状态码logText +=”response
status: “+ ctx.status +”n”;//响应内容logText +=”response body:
“+”n”+JSON.stringify(ctx.body) +”n”;//响应日志甘休logText
+=”*************** response log end
***************”+”n”;returnlogText;}//格式化错误日志varformatError
=function(ctx, err, resTime卡塔尔{varlogText
=newString(卡塔尔(قطر‎;//错误音信起首logText
+=”n”+”*************** error log start
***************”+”n”;//增多央求日志logText +=
formatReqLog(ctx.request, resTime卡塔尔国;//错误名称logText +=”err name: “+
err.name +”n”;//错误消息logText +=”err message: “+ err.message
+”n”;//错误详细情况logText +=”err stack: “+ err.stack
+”n”;//错误消息截至logText +=”*************** error log
end
***************”+”n”;returnlogText;};//格式化央求日志varformatReqLog
=function(req, resTimeState of Qatar{varlogText =newString(卡塔尔国;varmethod =
req.method;//访谈方法logText +=”request method: “+ method
+”n”;//央求原始地址logText +=”request originalUrl:  “+ req.originalUrl
+”n”;//客户端iplogText +=”request client ip:  “+ req.ip
+”n”;//带头时间varstartTime;//央浼参数if(method ===’GET’State of Qatar {       
logText +=”request query:  “+JSON.stringify(req.query卡塔尔国 +”n”;//
startTime = req.query.requestStartTime;}else{        logText +=”request
body: “+”n”+JSON.stringify(req.body) +”n”;// startTime =
req.body.requestStartTime;}//服务器响适那时候间logText +=”response time: “+
resTime +”n”;returnlogText;}module.exports = logUtil;

接下去改正app.js文本中的logger部分。

//log工具constlogUtil =require(‘./utils/log_util’卡塔尔(قطر‎;//
loggerapp.use(async(ctx, next卡塔尔国 => {//响应初始时间conststart
=newDate(卡塔尔(قطر‎;//响应间距时间varms;try{//起首步入到下叁此中间件awaitnext(卡塔尔国; 
  ms =newDate(卡塔尔(قطر‎ – start;//记录响应日志logUtil.logResponse(ctx, ms卡塔尔; 
}catch(errorState of Qatar {    ms =newDate(卡塔尔国 –
start;//记录极度日志logUtil.logError(ctx, error, msState of Qatar;  }}State of Qatar;

在此将await next();放到了贰个try
catch里面,那样前边的中间件有特别都能够在这里集中管理。

举个例子说你会将一部分API至极作为符合规律值再次来到给客商端,就足以在此集中实行拍卖。然后后边的中间件只要throw自定义的API分外就足以了。

在起步服务以前毫无遗忘先安装log4js插件:

$ npm install log4js –save

开发银行服务

$ npm start

那会儿会运维退步,调节台会输出未有公文或文件目录。原因是大家在构造内部就算配置了文件目录,不过并不曾开创连锁目录,解决的法子是手动创立连锁目录,可能在劳动运营的时候,确认一下目录是还是不是存在,假诺不设有则创设连锁目录。

3.3 起头化logs文件目录

先来校正一下log_config.js文件,让前面的创办进度更舒畅。

纠正后的代码:

varpath =require(‘path’卡塔尔;//日志根目录varbaseLogPath =
path.resolve(__dirname,’../logs’State of Qatar//错误日志目录varerrorPath
=”/error”;//错误日志文件名varerrorFileName
=”error”;//错误日志输出完整路线varerrorLog帕特h = baseLogPath + errorPath
+”/”+ errorFileName;// var errorLogPath = path.resolve(__dirname,
“../logs/error/error”State of Qatar;//响应日志目录varresponsePath
=”/response”;//响应日志文件名varresponseFileName
=”response”;//响应日志输出完整路线varresponseLogPath = baseLogPath +
responsePath +”/”+ responseFileName;// var responseLogPath =
path.resolve(__dirname, “../logs/response/response”);module.exports =
{“appenders”:   
[//错误日志{“category”:”errorLogger”,//logger名称”type”:”dateFile”,//日志类型”filename”:
errorLogPath,//日志输出地方”alwaysIncludePattern”:true,//是不是总是有后缀名”pattern”:”-yyyy-MM-dd-hh.log”,//后缀,每小时创造一个新的日记文件”path”:
errorPath//自定义属性,错误日志的根目录},//响应日志{“category”:”resLogger”,”type”:”dateFile”,”filename”:
responseLogPath,”alwaysIncludePattern”:true,”pattern”:”-yyyy-MM-dd-hh.log”,”path”:
responsePath          }   
],”levels”://设置logger名称对应的的日记等第{“errorLogger”:”EOdysseyRO瑞虎”,”resLogger”:”ALL”},”baseLogPath”:
baseLog帕特h//logs根目录}

然后打开bin/www文件,加多如下代码:

varfs =require(‘fs’);varlogConfig
=require(‘../config/log_config’);/**

* 鲜明目录是或不是留存,倘使荒诞不经则开创目录

*/varconfirmPath =function(pathStr){if(!fs.existsSync(pathStr)){     
fs.mkdirSync(pathStr);console.log(‘createPath: ‘+ pathStr);    }}/**

* 开首化log相关目录

*/varinitLogPath
=function(卡塔尔国{//创设log的根目录’logs’if(logConfig.baseLogPath卡塔尔{   
confirmPath(logConfig.baseLogPathState of Qatar//依据不一致的logType创立区别的文件目录for(vari
=0, len = logConfig.appenders.length; i < len;
i++卡塔尔国{if(logConfig.appenders[i].path){       
confirmPath(logConfig.baseLogPath + logConfig.appenders[i].path);     
}    }  }}initLogPath();

与此相类似每一遍运维服务的时候,都会去确定一下连锁的文件目录是还是不是存在,纵然不设有就创制连锁的文件目录。

如今在来运维服务。在浏览器访谈,能够观察项目中多了logs目录以至相关子目录,并爆发了生活文件。

图片 5

剧情如下:

[2016-10-31 12:58:48.832] [INFO] resLogger –

*************** response log start
***************

request method: GET

request originalUrl:  /

request client ip:  ::ffff:127.0.0.1

request query:  {}

response time: 418

response status: 200

response body:

“koa2 title

koa2 title

Welcome to koa2 title

*************** response log end
***************

能够依据本身的供给,定制相关的日志格式。

除此以外关于配置文件的选项能够参见log4js-node 
Appenders说明。

如需查看项目代码 –> 代码地址:

https://github.com/tough1985/hello-koa2

选择Tag -> step3

4 格式化输出

假定大家今日支出的是八个API服务接口,会有二个集结的响应格式,同期也期望发生API错误时统一错误格式。

4.1 创设八个API接口

为目前的服务加多多少个接口,贰个getUser三个registerUser。

先在脚下项目下创办多个app/controllers目录,在该目录下增添一个user_controller.js文件。

图片 6

代码如下:

//获取客商exports.getUser =async(ctx, next卡塔尔(قطر‎ => {    ctx.body =
{username:’阿,希爸’,age:30}}//客户注册exports.registerUser =async(ctx,
next卡塔尔 => {console.log(‘registerUser’, ctx.request.body卡塔尔(قطر‎;}

简简单单的模拟一下。getUser重返一个user对象,registerUser只是打字与印刷输出一下倡议参数。

接下去为那三个主意配置路由。

4.2 为API接口配置路由

咱俩期望服务的地方的结合是那要的

域名 + 端口号 /api/效能连串/具体端口

例如

127.0.0.1:3001/api/users/getUser

先来增添二个api的路由和此外路由暌违管理。在routes目录下开创二个api目录,添加user_router.js文本,代码如下:

varrouter =require(‘koa-router’)();varuser_controller
=require(‘../../app/controllers/user_controller’);router.get(‘/getUser’,
user_controller.getUser);router.post(‘/registerUser’,
user_controller.registerUser);module.exports = router;

如此那般就落成了getUserregisterUser扩充了路由安顿,个中getUserGET主意号召,registerUser是用POST措施呼吁。

接下去对users其一作用模块实行路由配置,在routes/api目录下增添一个index.js文本,代码如下:

varrouter =require(‘koa-router’)();varuser_router
=require(‘./user_router’);router.use(‘/users’, user_router.routes(),
user_router.allowedMethods());module.exports = router;

最后对api张开路由配置,在app.js文本中增添如下代码:

constapi =require(‘./routes/api’);……router.use(‘/api’, api.routes(),
api.allowedMethods());

起头服务,在浏览器中访问127.0.0.1:3001/api/users/getUser可以收获如下输出,表达配置成功。

{“username”:”阿,希爸”,”age”:30}

4.3 格式化输出

用作三个API接口,大家或然希望统一重回格式,举例getUser的出口给客户端的重返值是这么的:

{“code”:0,”message”:”成功”,”data”: {“username”:”阿,希爸”,”age”:30}}

按照koa的中间件施行顺序,大家要拍卖数据应该在发送响应以前和路由获得数码之后增添壹当中间件。在类型的根目录下增加一个middlewares目录,在该目录下增加response_formatter.js文本,内容如下:

/**

* 在app.use(router卡塔尔(قطر‎此前调用

*/varresponse_formatter =async(ctx, next卡塔尔(قطر‎ =>
{//先去试行路由awaitnext(卡塔尔(قطر‎;//若是有重返数据,将赶回数据增加到data中if(ctx.body卡塔尔国{        ctx.body = {code:0,message:’success’,data: ctx.body        }   
}else{        ctx.body = {code:0,message:’success’}    }}module.exports
= response_formatter;

然后在app.js中载入。

constresponse_formatter
=require(‘./middlewares/response_formatter’State of Qatar;…//增多格式化管理响应结果的中间件,在增添路由事情发生此前调用app.use(response_formatter);router.use(‘/’,
index.routes(), index.allowedMethods());router.use(‘/users’,
users.routes(), users.allowedMethods());router.use(‘/api’, api.routes(),
api.allowedMethods());app.use(router.routes(), router.allowedMethods());

运维服务,在浏览器中做客127.0.0.1:3001/api/users/getUser能够拿走如下输出,表达配置成功。

{“code”:0,”message”:”success”,”data”: {“username”:”阿,希爸”,”age”:30}}

4.4 对U揽胜L实行过滤

为啥必须求在router从前安装?

事实上在router之后设置也得以,可是必得在controller里面施行await
next(卡塔尔才会调用。也正是说什么人须要格式化输出结果自个儿手动调用。

在router前边设置也可能有一个难题,正是持有的路由响应输出都会举行格式化输出,那鲜明也不符合预期,那么大家要对UTucsonL进行过滤,通过过滤的才对她展开格式化处理。

再也更动一下response_formatter中间件,让她接纳叁个参数,然后再次回到二个async
function做为中间件。改变后的代码如下:

/**

* 在app.use(router卡塔尔(قطر‎早先调用

*/varresponse_formatter =(ctx卡塔尔=>{//借使有重回数据,将回来数据增进到data中if(ctx.bodyState of Qatar {       
ctx.body = {code:0,message:’success’,data: ctx.body        }    }else{ 
      ctx.body = {code:0,message:’success’}    }}varurl_filter
=function(pattern卡塔尔(قطر‎{returnasyncfunction(ctx, next卡塔尔(قطر‎{varreg
=newRegExp(pattern卡塔尔;//先去施行路由awaitnext(卡塔尔(قطر‎;//通过正则的url实行格式化管理if(reg.test(ctx.originalUrl卡塔尔卡塔尔{ 
          response_formatter(ctx);        }    }}module.exports =
url_filter;

app.js中对应的代码改为:

//仅对/api发轫的url进行格式化管理app.use(response_formatter(‘^/api’));

当今访谈127.0.0.1:3001/api/users/getUser如此以api在这里早先的地点都博览会开格式化管理,而任何的地点则不会。

4.5 API相当管理

要集中管理API格外,首先要开创二个API格外类,在app目录下新建二个error目录,添加ApiError.js文件,代码如下:

/**

* 自定义Api异常

*/classApiErrorextendsError{//布局方法constructor(error_name,
error_code,  error_message){super();this.name = error_name;this.code
= error_code;this.message = error_message;    }}module.exports =
ApiError;

为了让自定义Api非常能够越来越好的使用,大家制造一个ApiErrorNames.js文本来封装API格外消息,并得以通过API错误名称获取万分消息。代码如下:

/**

* API错误名称

*/varApiErrorNames = {};ApiErrorNames.UNKNOW_ERROR
=”unknowError”;ApiErrorNames.USER_NOT_EXIST =”userNotExist”;/**

* API错误名称对应的错误消息

*/consterror_map =newMap();error_map.set(ApiErrorNames.UNKNOW_ECRUISERROTucson,
{code:-1,message:’未知错误’}卡塔尔;error_map.set(ApiErrorNames.USER_NOT_EXIST,
{code:101,message:’顾客荒诞不经’}卡塔尔(قطر‎;//依据错误名称获取错误音信ApiErrorNames.getErrorInfo
=(error_name) =>{varerror_info;if(error_name) {        error_info
= error_map.get(error_nameState of Qatar;   
}//若无对症用药的错误消息,暗许’未知错误’if(!error_info) {       
error_name = UNKNOW_ERROR;        error_info =
error_map.get(error_name);    }returnerror_info;}module.exports =
ApiErrorNames;

修改ApiError.js文件,引入ApiErrorNames

ApiError.js

constApiErrorNames =require(‘./ApiErrorNames’);/**

* 自定义Api异常

*/classApiErrorextendsError{//布局方法constructor(error_name){super();varerror_info
= ApiErrorNames.getErrorInfo(error_name);this.name =
error_name;this.code = error_info.code;this.message =
error_info.message;    }}module.exports = ApiError;

response_formatter.js文本中管理API至极。

先引入ApiError:

var ApiError = require(‘../app/error/ApiError’);

下一场改良url_filter

varurl_filter =(patternState of Qatar =>{returnasync(ctx, next卡塔尔国 => {varreg
=newRegExp(pattern卡塔尔国;try{//先去奉行路由awaitnext(卡塔尔(قطر‎;        }catch(error){//假使万分类型是API分外並且经过正则验证的url,将错误新闻增多到响应体中回到。if(errorinstanceofApiError
&& reg.test(ctx.originalUrl卡塔尔){                ctx.status =200;         
      ctx.body = {code: error.code,message: error.message               
}            }//继续抛,让外层中间件管理日志throwerror;       
}//通过正则的url实行格式化管理if(reg.test(ctx.originalUrl卡塔尔国卡塔尔国{           
response_formatter(ctx);        }    }}

解释一下这段代码

利用try catch包裹await
next(卡塔尔国;,这样后边的中间件抛出的足够都可以在此几集中管理;

throw error;是为了让外层的logger中间件能够管理日志。

为了模仿运维作效果果,我们改正user_controller.js文件,内容如下:

constApiError =require(‘../error/ApiError’State of Qatar;constApiErrorNames
=require(‘../error/ApiErrorNames’卡塔尔(قطر‎;//获取客户exports.getUser =async(ctx,
next卡塔尔(قطر‎ => {//若是id != 1抛出API 异常if(ctx.query.id
!=1){thrownewApiError(ApiErrorNames.USER_NOT_EXIST);    }    ctx.body
= {username:’阿,希爸’,age:30}}

开发银行服务,在浏览器中访问127.0.0.1:3001/api/users/getUser能够获取结果如下:

{“code”:101,”message”:”顾客不设有”}

在浏览器中做客127.0.0.1:3001/api/users/getUser?id=1能够获取结果如下:

{“code”:0,”message”:”success”,”data”: {“username”:”阿,希爸”,”age”:30}}

如需查看项目代码 –> 代码地址:

https://github.com/tough1985/hello-koa2

选择Tag -> step4

5 测试

node使用主流的测量检验框架基本正是mochaAVA了,这里主要以mocha为功底举办创设相关的测量试验。

5.1 mocha

安装mocha

在终极输入

$ npm install –save-dev mocha

–dev表示只在development情况下增添信赖。

使用mocha

在项指标根目录下增添test目录,加多八个test.js文本,内容如下:

varassert =require(‘assert’);/**

* describe 测量检验套件 test suite 表示一组有关的测量试验

* it 测量检验用例 test case 表示八个独立的测量检验

* assert 断言 表示对结果的预料

*/describe(‘Array’,function(){    describe(‘#indexOf()’,function(){   
    it(‘should return -1 when the value is not present’,function(){     
      assert.equal(-1, [1,2,3].indexOf(4));        })    })});

在极限输入:

$ mocha

能够获取输出如下:

Array

#indexOf()

✓ should return -1 when the value is not present

1 passing (9ms)

mocha暗许运转test目录下的测量检验文件,测量检验文件通常与要测量试验的步子文件同名以.test.js作为后缀名。举个例子add.js的测量试验脚本名字便是add.test.js。

describe代表测量检验套件,种种测试脚本起码应该包罗叁个describe。

it意味着测验用例。

每个describe能够饱含多少个describe或多个it

assert是node提供的断言库。

assert.equal(-1, [1,2,3].indexOf(4));

那句代码的意味是我们意在[1,2,3].indexOf(4卡塔尔的值应该是-1,假使[1,2,3].indexOf(4卡塔尔(قطر‎的运作结果是-1,则透过测量试验,不然不经过。

能够把-1改成-2再试一下。

上边的例子是mocha提供的,mocha官网。

测验境遇

事情发生在此以前说过情况铺排的内容,大家要求执行测验的时候,加载相关的测验配置该如何是好?

在顶峰输入

$ NODE_ENV=test mocha

为了幸免每回都去输入NODE_ENV=test,可以改正package.json文本中的scripts.test改为:

“test”:”NODE_ENV=test mocha”,

从今以后运行测验直接输入npm test就能够了。

常用的参数

mocha在施行时方可教导过多参数,这里介绍多少个常用的。

–recursive

mocha默许试行test目录下的测验脚本,不过不会运营test下的子目录中的脚本。

想要施行子目录中的测验脚本,能够在运转时增加–recursive参数。

$ mocha –recursive

–grep

举例您写了累累测试用例,当你增多了八个新的测量检验,实施之后要在结果里面找半天。这种状态就能够虚构–grep参数。

–grep能够只实行单个测验用例,相当于进行某叁个it。举个例子将刚刚的测量检验修正如下:

describe(‘Array’, function() {

describe(‘#indexOf()’, function() {

it(‘should return -1 when the value is not present’, function(){

assert.equal(-1, [1,2,3].indexOf(4));

})

it(‘length’, function(){

assert.equal(3, [1, 2, 3].length);

})

})

});

加多了四个length测量检验用例,想要单独施行那么些测量试验用例即将在顶峰输入:

$ mocha –grep ‘length’

能够见到length用例被单独实践了。

这里有少数索要留意,因为大家安插了npm test,假如直白运维

$ npm test –grep ‘length’

与此相类似是不能够完毕效果的。

要给npm
scripts
本子传参供给先输入–然后在输入参数,所以想要实行下面的意义应该输入:

$ npm test — –grep ‘length’

关于mocha就轻便的介绍这么多,想要了然更加的多相关的剧情,推荐留神阅读叁遍阮一峰先生写的测量检验框架
Mocha
实例教程。

5.2 chai

chai是一个断言库。在此之前的例证中,我们利用的是node提供的断言库,他的效果非常少,基本上唯有equalokfail那样轻易的效果与利益,很难满意平时的供给。

mocha法定表示你爱用什么断言用如何断言,反正老子都支持。

选择chai是因为她对断言的二种语法都协理,并且功用也正如健全–>chai官网。

chai帮助should、expect和assert二种断言格局。

assert语法在此以前大家早已见过了,chai只是丰硕了功用,语法并未变动。

expectshould的语法更近乎自然语言的习贯,不过should接受的时候会产出部分匪夷所思的事态。所以相比常用的要么expect

官方的DEMO

varexpect =
chai.expect;expect(foo).to.be.a(‘string’);expect(foo).to.equal(‘bar’);expect(foo).to.have.length(3);expect(tea).to.have.property(‘flavors’) 
.with.length(3);

明显语法的可读性更加好,更临近人类的语言。

简轻易单的演说此中的to、be那样的语法。

chai使用了链式语法,为了使语法越发临近自然语言,增添了数不清抒发语义可是还没其他作用的词汇。

to

be

been

is

that

which

and

has

have

with

at

of

same

地点列出的这个词未有其余功能,只是为着提升语义。

也正是说

expect(1+1).to.be.equal(2)

expect(1+1).equal(2)

是完全相同的。

安装chai

在尖峰输入:

$ npm install –save-dev chai

使用chai

test目录下新建贰个chai.test.js文本,内容如下:

constexpect =require(‘chai’).expect;describe(‘chai expect
demo’,function(){    it(‘expect equal’,function(){       
expect(1+1).to.equal(2);        expect(1+1).not.equal(3);    });});

在终端输入:

$ npm test — –grep ‘expect equal’

获得输出:

chai expect demo

✓ expect equal

1 passing (6ms)

评释配置成功。有关chai的越来越多效果与利益请查看官方API
–>chai_api

5.3 supertest

现阶段大家得以选拔测量检验框架做一些大致的测验,想要测量试验接口的对应数额,将在采纳supertest了。

supertest入眼意义正是对HTTP进行测验。特别是对REST
API,我们对get必要相当的轻易模仿,不过post方法就很难(当然你也足以接受postman那样的插件)。

supertest可以照猫画虎HTTP的各样哀告,设置header,增加央浼数据,并对响应实行预见。

安装supertest

在终极输入:

$ npm install –save-dev supertest

使用supertest

大家对现成的多个API接口getUserregisterUser开展测量检验。在test目录下创办user_api.test.js文本,内容如下:

constrequest =require(‘supertest’);constexpect
=require(‘chai’).expect;constapp
=require(‘../app.js’);describe(‘user_api’, (卡塔尔 => {    it(‘getUser’,
(doneState of Qatar => {        request(app.listen(卡塔尔State of Qatar           
.get(‘/api/users/getUser?id=1’State of Qatar//get方法.expect(200State of Qatar//断言状态码为200.end((err,
resState of Qatar=>{console.log(res.body卡塔尔(قطر‎;//断言data属性是四个对象expect(res.body.data卡塔尔.to.be.an(‘object’卡塔尔; 
              done(State of Qatar;            }卡塔尔国;    }卡塔尔国    it(‘registerUser’, (done)=> {// 须求参数,模拟客户对象varuser = {username:’阿,希爸’,age:31} 
      request(app.listen(卡塔尔卡塔尔           
.post(‘/api/users/registerUser’卡塔尔(قطر‎//post方法.send(user卡塔尔(قطر‎//增多央求参数.set(‘Content-Type’,’application/json’卡塔尔//设置header的Content-Type为json.expect(200State of Qatar//断言状态码为200.end((err,
res卡塔尔(قطر‎=>{console.log(res.body卡塔尔;//断言再次回到的code是0expect(res.body.code卡塔尔.to.be.equal(0卡塔尔国; 
              done(State of Qatar;            }State of Qatar    }卡塔尔(قطر‎}State of Qatar

如若明天直接运行npm test举行测验会报错,原因是mocha默许是不支持async
await语法,解决的艺术是Babel

Babel的机要成效是对两样版本的js进行转码。

即便你对Babel不打听,请留神阅读贝布el
入门教程与Babel官网。

由于koa-generator已经帮大家抬高相关的Babel借助,大家只需求丰裕相关的平整就能够了。在类型的根目录下增添四个.babelrc文本,内容如下:

{

“env”: {

“test”: {

“presets”: [“es2015-node5”],

“plugins”: [

“transform-async-to-generator”,

“syntax-async-functions”

]

}

}

}

这段文件的意味是对当env=test时,应用es二零一五-node5、transform-async-to-generator、syntax-async-functions准绳举行转码。

Babel笔者们设置好了,想要mocha使用这几个法则还要在奉行时加多一个发令。

打开package.json,将scripts.test修改为:

“test”: “NODE_ENV=test mocha –compilers js:babel-core/register”,

在终端施行npm test,输出如下内容表达测量检验通过。

user_api

<– GET /api/users/getUser?id=1

–> GET /api/users/getUser?id=1 200 14ms 74b

{ code: 0,

message: ‘success’,

data: { username: ‘阿,希爸’, age: 30 } }

✓ getUser (57ms)

<– POST /api/users/registerUser

registerUser { username: ‘阿,希爸’, age: 31 }

–> POST /api/users/registerUser 200 2ms 30b

{ code: 0, message: ‘success’ }

✓ registerUser

有关supertest的越多用法请参照他事他说加以考察github_supertest。

如需查看项目代码 –> 代码地址:

https://github.com/tough1985/hello-koa2

选择Tag -> step5

作者:阿_希爸

链接:

來源:简书

作品权归作者全数。商业转发请联系作者得到授权,非商业转发请表明出处。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图