官方文档:https://koa.bootcss.com/
简介:由 Express 幕后的原班人马打造,没有捆绑任何中间件。KOA的大小完全由需求决定。
依赖:Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.

简单用法:

  1. const Koa = require('koa');
  2. const app = new Koa();
  3. app.use(async ctx => {
  4. ctx.body = 'Hello World';
  5. });
  6. //错误处理
  7. app.on('error', (err, ctx) => {
  8. log.error('server error', err, ctx)
  9. });
  10. app.listen(3000);

中间件

路由(koa-router)+解析参数(koa-bodyparse)

  1. const bodyParser = require('koa-bodyparser')
  2. const bodyParser = require('koa-bodyparser')
  3. const router = new Router(); // 实例化路由
  4. app.use(router.routes());
  5. app.use(bodyParser())
  6. /**post 用body */
  7. router.post('/api/user/login', async (ctx, next) => {
  8. let postData = await parsePostData(ctx);
  9. const user = configDb.get('user').value();
  10. if (user.name === postData.username && user.pwd === postData.password) {
  11. sendResponse(ctx, { token: user.id })
  12. console.log('登录成功');
  13. } else {
  14. sendResponse(ctx, undefined, -1, '用户名密码错误')
  15. console.log('登录失败');
  16. }
  17. next();
  18. })
  19. /** get 用 query */
  20. router.get('/api/cookies/get', async (ctx, next) => {
  21. sendResponse(ctx, { items: cookies });
  22. next();
  23. })
  24. // 解析 post 字符串为 json
  25. function parsePostData(ctx) {
  26. return new Promise((resolve, reject) => {
  27. try {
  28. let postdata = '';
  29. ctx.req.addListener('data', (data) => {
  30. postdata += data;
  31. });
  32. ctx.req.on("end", function() {
  33. console.log('1111' + postdata);
  34. resolve(JSON.parse(postdata));
  35. })
  36. } catch (error) {
  37. reject(error);
  38. }
  39. });
  40. }
  41. //格式化JSON返回值
  42. function sendResponse(ctx, data={}, code = 0, message = 'SUCCESS') {
  43. const res = JSON.stringify({ data, code, message })
  44. ctx.response.body = res;
  45. }

处理静态资源(koa-static)

  1. const KoaStatic = require('koa-static');
  2. const staticUtil = require('./static');
  3. app.use(KoaStatic('../dist'));//设置静态文件的路径
  4. app.use(async (ctx, next) => {
  5. console.log(ctx.url);
  6. if (ctx.url.startsWith('/api')) {
  7. next();
  8. return;
  9. }
  10. // 获取静态资源内容,有可能是文件内容,目录,或404
  11. let fullStaticPath = path.join(__dirname, '../dist')
  12. console.log(fullStaticPath);
  13. // 获取静态资源内容,有可能是文件内容,目录,或404
  14. let _content = await staticUtil.content(ctx, fullStaticPath)
  15. // 解析请求内容的类型
  16. let _mime = staticUtil.parseMime(ctx.url)
  17. // 如果有对应的文件类型,就配置上下文的类型
  18. if (_mime) {
  19. ctx.type = _mime
  20. }
  21. // 输出静态资源内容
  22. if (_mime && _mime.indexOf('image/') >= 0) {
  23. // 如果是图片,则用node原生res,输出二进制数据
  24. ctx.res.writeHead(200)
  25. ctx.res.write(_content, 'binary')
  26. ctx.res.end()
  27. } else {
  28. // 其他则输出文本
  29. ctx.body = _content
  30. }
  31. });

./static 文件

  1. const path = require('path')
  2. const fs = require('fs')
  3. let mimes = {
  4. 'css': 'text/css',
  5. 'less': 'text/css',
  6. 'gif': 'image/gif',
  7. 'html': 'text/html',
  8. 'ico': 'image/x-icon',
  9. 'jpeg': 'image/jpeg',
  10. 'jpg': 'image/jpeg',
  11. 'js': 'text/javascript',
  12. 'json': 'application/json',
  13. 'pdf': 'application/pdf',
  14. 'png': 'image/png',
  15. 'svg': 'image/svg+xml',
  16. 'swf': 'application/x-shockwave-flash',
  17. 'tiff': 'image/tiff',
  18. 'txt': 'text/plain',
  19. 'wav': 'audio/x-wav',
  20. 'wma': 'audio/x-ms-wma',
  21. 'wmv': 'video/x-ms-wmv',
  22. 'xml': 'text/xml'
  23. }
  24. /**
  25. * 遍历读取目录内容(子目录,文件名)
  26. * @param {string} reqPath 请求资源的绝对路径
  27. * @return {array} 目录内容列表
  28. */
  29. function walk(reqPath) {
  30. let files = fs.readdirSync(reqPath)
  31. let dirList = []
  32. let fileList = []
  33. for (let i = 0, len = files.length; i < len; i++) {
  34. let item = files[i]
  35. let itemArr = item.split('.')
  36. let itemMime = (itemArr.length > 1) ? itemArr[itemArr.length - 1] : 'undefined'
  37. if (typeof mimes[itemMime] === 'undefined') {
  38. dirList.push(files[i])
  39. } else {
  40. fileList.push(files[i])
  41. }
  42. }
  43. let result = dirList.concat(fileList)
  44. return result
  45. }
  46. /**
  47. * 封装目录内容
  48. * @param {string} url 当前请求的上下文中的url,即ctx.url
  49. * @param {string} reqPath 请求静态资源的完整本地路径
  50. * @return {string} 返回目录内容,封装成HTML
  51. */
  52. function dir(url, reqPath) {
  53. // 遍历读取当前目录下的文件、子目录
  54. let contentList = walk(reqPath)
  55. let html = `<ul>`
  56. for (let [index, item] of contentList.entries()) {
  57. html = `${html}<li data-index="${index}"><a href="${url === '/' ? '' : url}/${item}">${item}</a>`
  58. }
  59. html = `${html}</ul>`
  60. return html
  61. }
  62. /**
  63. * 读取文件方法
  64. * @param {string} 文件本地的绝对路径
  65. * @return {string|binary}
  66. */
  67. function file(filePath) {
  68. let content = fs.readFileSync(filePath, { encoding: 'utf-8' }, 'binary')
  69. return content
  70. }
  71. /**
  72. * 获取静态资源内容
  73. * @param {object} ctx koa上下文
  74. * @param {string} 静态资源目录在本地的绝对路径
  75. * @return {string} 请求获取到的本地内容
  76. */
  77. async function content(ctx, fullStaticPath) {
  78. // 封装请求资源的完绝对径
  79. let reqPath = path.join(fullStaticPath, ctx.url)
  80. // 判断请求路径是否为存在目录或者文件
  81. let exist = fs.existsSync(reqPath)
  82. // 返回请求内容, 默认为空
  83. let content = ''
  84. if (!exist) {
  85. // 如果请求路径不存在,返回404
  86. content = '请求路径不存在!'
  87. } else {
  88. // 判断访问地址是文件夹还是文件
  89. let stat = fs.statSync(reqPath)
  90. if (stat.isDirectory()) {
  91. // 如果为目录,则渲读取目录内容
  92. content = dir(ctx.url, reqPath)
  93. } else {
  94. // 如果请求为文件,则读取文件内容
  95. content = await file(reqPath)
  96. }
  97. }
  98. return content
  99. }
  100. // 解析资源类型
  101. function parseMime(url) {
  102. let extName = path.extname(url)
  103. extName = extName ? extName.slice(1) : 'unknown'
  104. return mimes[extName]
  105. }
  106. module.exports = {
  107. parseMime: parseMime,
  108. content: content
  109. }