解决koa2 ctx.render 463.comis not a function报错问题

报错时koa.js代码如下

后端代码

入口文件:demo/upload-async/index.js

const Koa = require('koa');
const views = require('koa-views');
const path = require('path');
const convert = require('koa-convert');
const static = require('koa-static');
const {uploadFile} = require('./util.upload');

const app = new Koa();

/**
 * 使用第三方中间件 start
 */
app.use(views(path.join(__dirname, './view'), {
  extension: 'ejs'
}));

// 静态资源目录对于相对于入口文件index.js的路径
const staticPath = './static';
// 由于koa-static目前不支持koa2
// 所以只能用koa-convert封装一下
app.use(convert(static(
  path.join(__dirname, staticPath);
)));

/**
 * 使用第三方中间件 end
 */
app.use(async(ctx) => {
  if(ctx.method === 'GET') {
    let title = 'upload pic async';
    await ctx.render('index', {
      title;
    });
  } else if(ctx.url === '/api/picture/upload.json' && ctx.method === 'POST') {
    // 上传文件请求处理
    let result = {success: false};
    let serverFilePath = path.join(__dirname, 'static/image');

    // 上传文件事件
    result = await uploadFile(ctx, {
      fileType: 'album',
      path: serverFilePath;
    });
    ctx.body = result;
  } else {
    // 其他请求显示404
    ctx.body = '<h1>404!!! o(╯□╰)o</h1>';
  }
});

app.listen(3000, () => {
  console.log('[demo] upload-pic-async is starting at port 3000');
});

后端上传图片流写操作 入口文件 demo/upload-async/util/upload.js

const inspect = reuqire('util').insepect;
const path = requie('path');
const os = require('os');
const fs = require('fs');
const Busboy = require('busboy');

/**
 * 同步创建文件目录
 * @param {string} dirname 目录绝对地址
 * @return {boolean} 创建目录结果
 */
function mkdirSync(dirname) {
  if(fs.existsSync(dirname)) {
    return true;
  } else {
    if(mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname);
      return true;
    }
  }
}

/**
 * 获取上传文件的后缀名
 * @param {string} filename 获取上传文件的后缀名
 * @return {string} 文件后缀名
 */
function getSuffixName(fileName) {
  let nameList = fileName.split('.');
  return nameList[nameList.length - 1];
}

/**
 * 上传文件
 * @param {object} ctx koa上下文
 * @param {object} options 文件上传参数: fileType文件类型,path文件存放路径
 * @return {promise} 创建目录结果
 */
function mkdirsSync(dirname) {
  if(fs.existsSync(dirname)) {
    return true;
  } else {
    if(mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname);
      return true;
    }
  }
}

/**
 * 获取上传文件的后缀名
 * @param {string} fileName 获取上传文件的后缀名
 * @return {string} 文件后缀名
 */
function getSuffixName(fileName) {
  let nameList = fileName.split('.');
  return nameList[nameList.length - 1];
}

/**
 * 上传文件
 * @param {object} ctx  koa上下文
 * @param {object} options 文件上传参数 fileType文件类型,path文件存放路径
 * @return {promise} 
 */
function uploadFile(ctx, options) {
  let req = ctx.req;
  let res = ctx.res;
  let busboy = new Busboy({headers: req.headers});

  //  获取类型
  let fileType = options.fileType || 'common';
  let filePath = path.join(options.path, fileType);
  let mkdirResult = mkdirsSync(filePath);

  return new Promise((reslove, reject) => {
    console.log('文件上传中...');
    let result = {
      success: false,
      message: '',
      data: null
    }

    //  解析请求文件事件
    busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
      let fileName = Math.random().toString(16).substr(2) + '.' + getSuffixName(filename);
      let _uploadFilePath = path.join(filePath, fileName);
      let saveTo = path.join(_uploadFilePath);

      //  文件保存到制定路径
      file.pipe(fs.createWriteStream(saveTo));

      //  文件写入事件结束
      file.on('end', function() {
        result.success = true;
        result.message = '文件上传成功';
        result.data = {
          pictureUrl: `//${ctx.host}/image/${fileType}/${fileName}`;
        }
        console.log('文件上传成功!');
        resolve(result);
      });
    });

    //  解析结束事件
    busboy.on('finish', function() {
      console.log('文件上结束');
      resolve(result);
    });

    //  解析错误事件
    busboy.on('error', function(err) {
      console.log('文件上出错');
      reject(result);
    });

    req.pipe(busboy);
  });
}

module.exports = {
  uploadFile
}

463.com 1 

使用方法

koa提供了从上下文直接读取,写入cookie的方法

  • ctx.cookies.get(name, [options])读取上下文请求中的cookie
  • ctx,cookies.set(name, value, [options])在上下文中写入cookie
    koa2中操作的cookies是使用了npm的cookies模块,源码在:https://github.com/pillarjs/cookies,所以在读写cookie的使用参数与该模块的使用一致。

view是前端页面文件夹,使用的模板引擎是pug

Promise封装mysql模块

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

util/file.js
const fs = require('fs');

/**
 * 读取文件方法
 * @param {string} 文件本地的绝对路径
 * @return {string|binary} 
 */
function file(filePath) {
  let content = fs.readFileSync(filePath, 'binary');
  return content;
};

module.exports = file;

项目路径如下

util/dir.js
const url = require('url');
const fs = require('fs');
const path = require('path');

//  遍历读取目录内容方法
const walk = require('./walk');

/**
 * 封装目录内容
 * @param {string} url 当前请求的上下文中的url,即:ctx.url
 * @param {string} reqPath 请求静态资源的完整本地路径
 * @return {string} 返回目录内容,封装成HTML
 */
function dir(url, reqPath) {
  //  遍历读取当前目录下的文件,子目录
  let contentList = walk(reqPath);

  let html = `<ul>`;
  for(let [index, item] of contentList.entries()) {
    html = `${html}<li><a href="${url === '/' ? '' : url}">${item}</a></li>`
  }
  html = `${html}</ul>`;

  return html;
}

module.exports = dir;

造成这个bug的原因是因为中间件的执行是有顺序的,路由在前,然后模板引擎在后的话,当执行到ctx.render的时候,模板引擎相关的中间件还未执行,render方法还未绑定到ctx上,所以就会报ctx.render
is not a function

运行结果

访问http://localhost:3000/index

  • 可以在控制台的cookie列表中看到写在页面上的cookie
  • 在控制台的console中使用document.cookie可以打印出页面的所有cookie(需要是httpOnly设置false才能显示)
    浏览器显示:

cookie is ok

console控制台显示:

document.cookie
"cid=hello world"

最近在学习使用koa2,在尝试用koa2复写之前用express写的一个入口文件的时候发现命令行报错ctx.render
is not a function

官方文档

busboy
API:https://www.npmjs.com/package/busboy

其实这个bug很好解决,那就是把配置模板引擎的代码移动到所有与路由相关的代码之前,在这里就应该修改为

用例详解

您可能感兴趣的文章:

koa2原生路由实现

app.js是之前用express写的入口文件

原生koa2实现静态资源服务器代码实例
//使用koa复写入口文件
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const views = require('koa-views');
const serve = require('koa-static');

const app = new Koa();
const router = new Router();

app.use(serve(__dirname));
app.use(koaBody()).use(router.routes());
app.use(views(__dirname + '/views/pages',{
  extension: 'pug'
}))

app.listen(3000);

router.get('/', async(ctx, next) => {
  await ctx.render("index",{
    title:"nodeWeb 首页"
  })
});
async中间件开发

koa.js是用koa2复写的入口文件

GET请求数据获取

463.com 2 

打开chrome浏览器的node调试窗口

在Sources中的node_modules文件夹下的index.js文件可以看到如下代码:

(function(exports, require, module, __filename, __dirname) {
  const Koa = require('koa');
  const app = new Koa();

  app.use(async (ctx) => {
    ctx.body = 'hello koa2';
  });

  app.listen(3000, () => {
    console.log('[demo] start-quick is starting at port 3000');
  });
});

打开了node的调试窗口后,原来绿色的node按钮变为灰色,同时调试框会显示debug状态

Debugger attached
//使用koa复写入口文件
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const views = require('koa-views');
const serve = require('koa-static');

const app = new Koa();
const router = new Router();

app.use(serve(__dirname));
//配置模板引擎
app.use(views(__dirname + '/views/pages',{
  extension: 'pug'
}))
//使用koa-router
app.use(koaBody()).use(router.routes());

app.listen(3000);

//设置路由
router.get('/', async(ctx, next) => {
  await ctx.render("index",{
    title:"nodeWeb 首页"
  })
});
例子目录
├── index.js # api文件
├── package.json
└── test # 测试目录
    └── index.test.js # 测试用例
``
#####所需测试demo
```javascript
const Koa = require('koa');
const app = new Koa();

const server = async (ctx, next) => {
  let result = {
    success: true,
    data: null
  };

  if(ctx.method === 'GET') {
    if(ctx.url === '/getString.json') {
      result.data = 'this is string data';
    } else if(ctx.url === '/getNumber.json') {
      result.data = 123456;
    } else {
      result.success = false
    }
    ctx.body = result;
    next && next();
  } else if(ctx.method === 'POST') {
    if(ctx.url === '/postData.json') {
      result.data = 'ok';
    } else {
      result.success = false;
    }
    ctx.body = result;
    next && next();
  } else {
    ctx.body = 'hello world';
    next && next();
  }
};

app.use(server);

module.exports = app;

app.listen(3000, () => {
  console.log('[demo] test-unit is starting at port 3000');
});

启动服务后访问接口,看到以下数据:
http://localhost:3000/getString.json

{
  "success": true,
  "data": "this is string data"
}
源码目录
├── index.js # 程序入口文件
├── node_modules/
├── package.json
├── sql   # sql脚本文件目录
│   ├── data.sql
│   └── user.sql
└── util    # 工具代码
    ├── db.js # 封装的mysql模块方法
    ├── get-sql-content-map.js # 获取sql脚本文件内容
    ├── get-sql-map.js # 获取所有sql脚本文件
    └── walk-file.js # 遍历sql脚本文件

koa2快速开始

启动脚本
node --inspect index.js

cookie/session

运行demo
指令框显示

指令框就会出现以下字样:

Debugger listening on ws://127.0.0.1:9229/4c23c723-5197-4d23-9b90-d473f1164abe
For help see https://nodejs.org/en/docs/inspector

使用chrome浏览器调试server
访问:http://localhost:3000
打开浏览器调试窗口可以看到一个node.js的小logo

异步上传图片实现

具体流程
    +---------------------------------------------------+
       |                                                   |
       |   +-----------+   +-----------+   +-----------+   |
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
+----------+  遍历sql  +---+ 解析所有sql +---+  执行sql  +------------>
       |   |  目录下的  |   |  文件脚本  |   |   脚本     |   |
+----------+  sql文件   +---+   内容    +---+           +------------>
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
       |   |           |   |           |   |           |   |
       |   +-----------+   +-----------+   +-----------+   |
       |                                                   |
       +---------------------------------------------------+
代码目录
├── static # 静态资源目录
│   ├── css/
│   ├── image/
│   ├── js/
│   └── index.html
├── util # 工具代码
│   ├── content.js # 读取请求内容
│   ├── dir.js # 读取目录内容
│   ├── file.js # 读取文件内容
│   ├── mimes.js # 文件类型列表
│   └── walk.js # 遍历目录内容
└── index.js # 启动入口文件
util.mime.js
let mimes = {
  'css' : 'text/css',
  'less' : 'text/css',
  'gif': 'image/gif',
  'html' : 'text/html',
  'ico' : 'image/x-icon',
  'jpeg' : 'image/jpeg',
  'jpg' : 'image/jpeg',
  'js' : 'text/javascript',
  'json' : 'application/json',
  'pdf' : 'application/pdf',
  'png' : 'image/png',
  'svg' : 'images/svg+xml',
  'swf' : 'application/x-shockwave-flash',
  'tiff' : 'image/tiff',
  'txt' : 'text/plain',
  'wav' : 'audio/x-wav',
  'wma' : 'audio/x-ms-wma',
  'wmv' : 'video/x-wmv',
  'xml' : 'text/xml'
}

module.exports = mimes;

koa-jsonp中间件

koa.js官方wiki中介绍了koa-jsonp中间件,它支持koa2,使用方式简单。

运行结果

浏览器访问http://localhost:3000

http://localhost:3000/upload.json

{
  'success': true,
  'formData': {
    'picName': "'hello world'";
  },
  'message': '文件上传成功';
}

快速开始

测试

获取sql目录详情 ./util/get-sql-map.js
const fs = require('fs');
const walkFile = require('./walk-file');

/**
 * 获取sql目录下的文件目录数据
 * @return {object}
 */
function getSqlMap() {
  let basePath = __dirname;
  basePath - basePath.replace(/\\/g, '\/');

  let pathArr = basePath.split('\/');
  pathArr = pathArr.splice(0, pathArr.length - 1);
  basePath = pathArr.join('/') + '/sql/';

  let fileList = walkFile(basePath, 'sql');
  return fileList;
}

module.exports = getSqlMap;
使用方法

在koa中,获取GET请求数据的源头是koa中request对象中的query方法或querystring方法。其中:query返回的是格式化好的参数对象,而与之对应querystring返回的是请求字符串,由于ctx对request的API有直接引用的方式,所以获取GET请求数据由两个途径:

    1. 是从上下文中直接获取:
    • 请求对象为ctx.query,返回如:{a:1, b:2}
    • 请求字符串ctx.querystring,返回如a=1&b=2
    1. 是从上下文的request对象中获取:
    • 请求对象ctx.request.query,返回如:{a:1, b:2}
    • 请求字符串ctx.request.querystring,返回如:a=1&b=2
实现JSONP
启动例子
node post-middleware.js

访问页面http://localhost:3000

入口文件 ./index.js
const fs = require('fs');
const getSqlContentMap = require('./util/get-sql-content-map');
const {query} = require('./util/db');

// 打印脚本执行日志
const eventLog = function(err, sqlFile, index) {
  if(err) {
    console.log(`[ERROR] sql脚本文件: ${sqlFile} 第${index + 1}条脚本 执行失败 o(╯□╰)o !`);
  } else {
    console.log(`[SUCCESS] sql脚本文件: ${sqlFile} 第${index + 1}条脚本 执行成功 O(∩_∩)O !`);    
  }
}

//  获取所有sql脚本内容
let sqlContentMap = getSqlContentMap();

//  执行建表sql脚本
const createAllTables = async () => {
  for(let key in sqlContentMap) {
    let sqlShell = sqlContentMap[key];
    let sqlShellList = sqlShell.split(';');

    for(let [i, shell] of sqlShellList.entries()) {
      if(shell.trim()) {
        let result = await query(shell);
        if(result.serverStatus * 1 === 2) {
          eventLog(null, key, i);
        } else {
          eventLog(true, key, i);
        }
      }
    }
  }
  console.log('sql脚本执行结束!');
  console.log('请按ctrl + c键退出!');
}

createAllTables();
前言

测试是一个项目周期里必不可少的环节,开发者在开发过程中也是无时无刻不在进行“人工测试”,如果每次修改一点代码,都要牵一发动全身来手动测试关联接口,这样会禁锢生产力。为了解放大部分测试生产力,相关的测试框架应运而生,比较出名的有mocha,karma,jasmine等。虽然框架繁多,但是使用起来都是大同小异。

模块简介

busboy模块是用来解析POST请求中node原生req中的文件流的模块。

执行结果

在终端上sql脚本依次执行成功的消息,逐条显示。

前言

一个http请求访问web服务静态资源,一般响应结果有3种情况:

  • 访问文本,例如:js, css. png, jpg, gif
  • 访问静态目录
  • 找不到资源,抛出404错误
解析原理
  • JSONP跨域输出的数据是可执行的JavaScript代码
    • ctx输出的类型应该是’text/javascript’
    • ctx输出的内容为可执行的返回数据JavaScript代码字符串
  • 需要有回调函数名callbackName,前端获取后会通过动态执行JavaScript代码字符,获取里面的数据。
测试套件、用例
  • describe()描述的是一个测试套件
  • 嵌套在describe()的it()是对接口进行自动化测试的测试用例
  • 一个describe()可以包含多个it()

describe('开始测试demo的GET请求', () => {
  it('测试/getString.json请求', () => {
    //TODO ...
  });
});
  • supertest封装服务request,是用来请求接口
  • chai.expect使用来判断测试结果是否与预期一样
    • chai断言有很多种方法,这里只是使用了数据型断言
generator中间件开发
  generator中间件返回的函数应该是function *()函数

/* ./middleware/logger-generator.js */
function log(ctx) {
  console.log(ctx.method, ctx.header.host + ctx.url);
}

module.exports = function() {
  return function *(next) {
    //  执行中间件操作
    log(this);

    if(next) {
      yield next
    }
  }
}
原理

对于POST请求的处理,koa2没有封装获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req,将POST表单数据解析为query
string(形式:a=1&b=2&c=3),再将query string解析成JSON格式(形式:{“a” :
“1”, “b” : “2”, “c” : “3”})。
注意:ctx.request是context经过封装的请求对象,ctx.req是context提供的node.js原生HTTP请求对象,同理ctx.response是context经过封装的响应对象,ctx.res是context提供的node.js原生HTTP请求对象。

sql脚本文件 ./sql/user.sql
CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `nick` varchar(255) DEFAULT NULL,
  `detail_info` json DEFAULT NULL,
  `create_time` varchar(20) DEFAULT NULL,
  `modified_time` varchar(20) DEFAULT NULL,
  `level` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf-8;

INSERT INTO 'user' set email=`1@example.com`, password=`123456`;
INSERT INTO 'user' set email=`2@example.com`, password=`123456`;
INSERT INTO 'user' set email=`3@example.com`, password=`123456`;
依赖模块
async/await
const {query} = require('./async-db');
async function selectAllData() {
  let sql = 'SELECT * FROM my_table';
  let dataList = await query(sql);
  return dataList;
}

async function getData() {
  let dataList = await selectAllData();
  console.log(dataList);
}

getData();
可以自定义打断电了

可以在浏览器开发者模式下的Sources资源中的index.js中打调试断点了。

async/await语法特点:
  • 可以让异步逻辑用同步写法实现
  • 最底层的await返回需要是Promise对象
  • 可以通过多层async function的同步写法来代替传统的callback嵌套
代码实例
const Koa = require('koa');
const app = new Koa();
const bodyParser = require('koa-bodyparser');

//  使用ctx.body解析中间件
app.use(bodyParser());

app.use(async(ctx) => {
  if(ctx.url === '/' && ctx.method === 'GET') {
    //  当GET请求时候返回表单页面
    let html = `
      <h1>koa2 request post demo</h1>
      <form method="POST" action="/">
        <p>userName</p>
        <input name="userName" /><br/>
        <p>nickName</p>
        <input name="nickName" /><br/>
        <p>email</p>
        <input name="email" /><br/>
        <button type="submit">submit</button>
      </form>
    `

    ctx.body = html;
  } else if(ctx.url === '/' && ctx.method === 'POST') {
    //  当POST请求的时候,中间件koa-bodyparser解析POST表单里的数据,并显示出来
    let postData = ctx.request.body,
        ctx.body = postData;
  } else {
    // 其他请求显示404
    ctx.body = '<h1>404!!! o(╯□╰)o</h1>';
  }
});

app.listen(3000, () => {
  console.log('[demo] request post is starting at port 3000');
});
原理

对于POST请求的处理,koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中。

摘录自:https://chenshenhai.github.io/koa2-note/
源码实例
├── index.js # 后端启动文件
├── node_modules
├── package.json
├── static # 静态资源目录
│   ├── image # 异步上传图片存储目录
│   └── js
│       └── index.js # 上传图片前端js操作
├── util
│   └── upload.js # 后端处理图片流操作
└── view
    └── index.ejs # ejs后端渲染模板

koa-router中间件

如果依靠ctx.request.url去手动处理路由,将会处理很多处代码,这时候就需要对应路由的中间件对路由进行控制,这里介绍一个比较好用的中间件koa-router。

简单例子
const Koa = require('koa');
const app = new Koa();

app.use(async(ctx) => {
  let url = ctx.request.url,
      ctx.body = url;
});

app.listen(3000);

访问http://localhost:3000/hello/world页面会输出
/hello/world,也就是说上下文的请求request对象中url之就是当前访问的路径名称,可以根据ctx.request.url
通过一定的判断或者正则匹配就可以定制出所需要的路由。

定制化路由
使用模板引擎
ejs模板引擎

ejs官方文档:https://github.com/mde/ejs

async/await使用

generator中间件开发
解析出POST请求上下文中的表单数据
//  解析上下文里node原声请求的POST参数
function parsePostData(ctx) {
  return new Promise((resolve, reject) => {
    try {
      let postdata = "";
      ctx.req.addListener('data', (data) => {
        postdata += data;
      });
      ctx.req.addListener('end', function() {
        let parseData = parseQueryStr(postdata);
        resolve(parseData);
      });
    } catch(err) {
      reject(err);
    }
  })
}

//  将POST请求参数字符串解析为JSON
function parseQueryStr(queryStr) {
  let queryData = {};
  let queryStrList = queryStr.split('&');
  console.log(queryStrList);
  for(let [index, queryStr] of queryStrList.entries()) {
    let itemList = queryStr.split('=');
    queryData[itemList[0]] = decodeURIComponent(itemList[1]);
  }
  return queryData;
}
执行运行脚本(harmony模式):
  node -harmony index.js
启动服务
node index.js
安装
npm install --save busboy
前言

在项目复杂的业务场景下,有时候需要在前端跨域获取数据,这时候提供数据的服务就需要提供跨域请求的接口,通常是JSONP方式提供跨域接口。

文件目录
├── package.json
├── index.js
└── view
    └── index.ejs
快速使用koa-router
const Koa = require('koa');
const fs = require('fs');
const app = new Koa();

const Router = require('koa-router');

let home = new Router();

//  子路由1
home.get('/', async(ctx) => {
  let html = `
    <ul>
      <li>
        <a href="/page/helloworld">
          /page/helloworld
        </a>
      </li>
      <li>
        <a href="/page/404">
          /page/404
        </a>
      </li>
    </ul>
  `;

  ctx.body = html;
});

// 子路由2
let page = new Router();
page.get('/404', async(ctx) => {
  ctx.body = '404.page!';
}).get('/helloworld', async(ctx) => {
  ctx.body = "helloworld page!";
});

//  装载所有子路由
let router = new Router();
router.use('/', home.routers(), home.allowedMethods()); 
router.use('/page', page.routes(), page.allowedMethods());

//  加载路由中间件
app.use(router.routes()).use(router.allowedMethods());

app.listen(3000, () => {
  console.log('[demo] route-use-middleware is starting at port 3000');
});
提交表单发起POST请求结果显示
{
    "userName": "koajs",
    "nickName": "noder",
    "email": "123@example.com"
}
效果

访问http://localhost:3000

+ css
+ image
+ js
+ index.html

访问http://localhost:3000/index.html
页面加载静态资源:显示有样式的文字和图片。
访问http://localhost:3000/js/index.js

(function() {
 alert('hello koa2 static server');
 console.log('hello koa2 static server');
})

请求数据获取

开始写测试用例

demo/test-unit/test/index.test.js

const supertest = require('supertest');
const chai = require('chai');
const app = require('./../index');

const expect = chai.expect
const request = supertest(app.listen());

//  测试套件/组
describe('开始测试demo的GET请求', () => {
  //  测试用例
  it('开始测试demo的GET请求', (done) => {
    request
      .get('/getString.json')
      .expect(200)
      .end((err, res) => {
        //  断言判断结果是否是object类型
        expect(res.body).to.be.an('object');
        expect(res.body.success).to.be,an('boolean');
        expect(req.body.data).to.be.an('string');
        done();
      });
  });
});

框架提出的背景

async中间件在koa@2中使用
async中间件只能在koa v2中使用

const  Koa = require('koa');  // koa v2
const loggerAsync = require('./middleware/logger-async');
const app = new Koa();

app.use(loggerAsync());

app.use((ctx) => {
  ctx.body = 'hello world!';
});

app.listen(3000);
console.log('the server is starting at port 3000');

开发debug

效果截图
安装node.js的mysql模块
npm install --save mysql
查看数据库session是否存储
mysql> use koa_demo;
mysql> show tables;
mysql> select * from _mysql_session_store;

路由

mysql模块

数据库操作文件 ./util/db.js

···javascript
const mysql = require(‘mysql’);

const pool = mysql.createPool({
host: ‘127.0.0.1’,
user: ‘root’,
password: ‘abc123’,
database: ‘koa_demo’
});

let query = function(sql, values) {
return new promise((resolve, reject) => {
pool.getConnection(function(err, connection) {
if(err) {
reject(err);
} else {
connection.query(sql, values, (err, rows) => {
if(err) {
reject(err);
} else {
resolve(rows);
}
connection.release();
});
}
});
});
}

module.exports = {
query
};

######获取所有sql脚本内容 ./util/get-sql-content-map.js
```javascript
const fs = require('fs');
const getSqlMap = require('./get-sql-map');

let sqlContentMap = {};

/**
 * 读取sql文件内容
 * @param {string} filename 文件名称
 * @param {string} path 文件所在的目录
 * @return {string} 脚本文件内容
 */
function getSqlContent(fileName, path) {
  let content = fs.readFileSync(path, 'binary');
  sqlContentMap[fileName] = content; 
}

/**
 * 封装所有sql文件脚本内容
 * @return {object}
 */
function getSqlContentMap() {
  let sqlMap = getSqlMap();
  for(let key in sqlMap) {
    getSqlContent(key, sqlMap[key]);
  }

  return sqlContentMap;
}

module.exports = getSqlContentMap;
安装koa2版本的koa-bodyparse@3中间件
npm install --save koa-bodyparser@3
前端代码

页面代码:

<buttton class="btn" id="J_UploadPictureBtn">上传图片</buttton>
<hr/>
<p>上传进度0%</p>
<p>上传结果图片</p>
<div id="J_PicturePreview" class="preview-picture">
</div>
<script src="/js/index.js"></script>

上传操作代码:

(function() {
  let btn = document.getElementById('J_UploadPictureBtn');
  let progressElem - document.getElementById('J_UploadProgress');
  let previewElem = document.getElementById('J_PicturePreview');

  btn.addEventListener('click', function() {
    uploadAction({
      success: function(result) {
        console.log(result)
        if(result && result.success && result.data && result.data.pictureUrl) {
          previewElem.innerHTML = '<img src="'+ result.data.pictureUrl +'" style="max-width: 100%">';
        }
      },
      progress: function(data) {
        if(data && data * 1 > 0) {
          progressElem.innerText = data;
        }
      }
    });
  });

  /**
   * 类型判断
   * @tyoe {Object}
   */
  let UtilType = {
    isPrototype: function(data) {
      return Object.prototype.toString.call(data).toLowerCase();
    },
    isJSON: function(data) {
      return this.isPrototype(data) === '[object object]';
    },
    isFunction: function(data) {
      return this.isPrototype(data) === '[object function]';
    }
  }

  /**
   * form表单上传请求事件
   * @param {object} options 请求参数
   */
  function requestEvent(options) {
    try {
      let formData = options.formData;
      let xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if(xhr.readyState === 4 && xhr.status === 200) {
          option.success(JSON.parse(xhr.responseText));
        }
      }
      xhr.upload.onprogress = function(evt) {
        let loaded = evt.loaded;
        let tot = evt.total;
        let per = Math.floor(100 * loaded/tot);
        options.progress(per);
      }
      xhr.open('post', '/api/picture/upload.json');
      xhr.send(formData);
    } catch(err) {
      options.fail(err);
    }
  }

  /**
   * 上传事件
   * @param {object} options 上传参数
   */
  function uploadEvent(options) {
    let file;
    let formData = new formData();
    let input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('name', 'files');

    input.click();
    input.onchange = function() {
      file = input.files[0];
      formData.append('files', file);

      requestEvent({
        formData,
        success: options.success,
        fail: options.fail,
        progress: options.progress
      });
    }
  }

  /**
   * 上传操作
   * @param {object} options 上传参数
   */
  function uploadAction(options) {
    if(!UtilType.isJSON(options)) {
      console.log('upload options is null');
    };
    let _options = {};
    _options.success = UtilType.isFunction(options.success) ? options.success : function() {};
    _options.fail = UtilType.isFunction(options.fail) ? options.fail :function() {};
    _options.progress = UtilType.isFunction(options.progress) ? options.progress : function() {};

    uploadEvent(_options);
  }
})();
安装koa2
//  初始化package.json
npm init

//  安装koa2
npm install koa
koa2特性
  • 只提供封装好的http上下文、请求、响应,以及基于async/await的中间件容器。
  • 利用ES7的async/await模式来处理传统的回调嵌套问题和代替koa@1中的generator,但是需要在Node
    7.x版本上的harmony模式下才能支持async/await。
  • 中间件只支持async/await形式的封装,如果需要使用koa@1中基于generator的中间件,需要通过中间件koa-convert封装一下才能使用。
启动demo

由于koa2是基于async/await操作中间件,目前node.js
7.x的harmony模式下才能使用,所以启动的时的脚本如下:

node index.js

浏览器访问http:localhost:3000](http://localhost:3000/

async/await封装使用mysql

generator中间件在koa@1中的使用
  generator中间件在koa v1中可以直接use使用

const koa = require('koa'); // koa v1
const loggerGenerator = require('./middleware/loggerGenerator');
const app = koa();

app.use(loggerGenerator());

app.use(function *() {
  this.body = "hello world!";
});

app.listen(3000);

console.log('the server is starting at port 3000');
运行实例
创建数据库会话
const mysql = require('mysql');
const connection = mysql.createConnection({
  host: '127.0.0.1',  // 数据库地址
  user: 'root', //  数据库用户
  password: '123456'. //  数据库密码
  database: 'my_database' //  选中数据库
});

//  执行sql脚本对数据库进行读写
connection.query('SELECT * FROM my_table', (error, result, fields) => {
  if(error) throw error
  //  connected!

  //  结束会话
  connection.release();
});

注意:一个事件就有一个从开始到结束的过程,数据库会话操作执行完成后,就需要关闭掉,以免占用连接资源。

相关文章