官方文档:https://koa.bootcss.com/
简介:由 Express 幕后的原班人马打造,没有捆绑任何中间件。KOA的大小完全由需求决定。
依赖:Koa 依赖 node v7.6.0 或 ES2015及更高版本和 async 方法支持.
简单用法:
const Koa = require('koa');const app = new Koa();app.use(async ctx => {ctx.body = 'Hello World';});//错误处理app.on('error', (err, ctx) => {log.error('server error', err, ctx)});app.listen(3000);
中间件
路由(koa-router)+解析参数(koa-bodyparse)
const bodyParser = require('koa-bodyparser')const bodyParser = require('koa-bodyparser')const router = new Router(); // 实例化路由app.use(router.routes());app.use(bodyParser())/**post 用body */router.post('/api/user/login', async (ctx, next) => {let postData = await parsePostData(ctx);const user = configDb.get('user').value();if (user.name === postData.username && user.pwd === postData.password) {sendResponse(ctx, { token: user.id })console.log('登录成功');} else {sendResponse(ctx, undefined, -1, '用户名密码错误')console.log('登录失败');}next();})/** get 用 query */router.get('/api/cookies/get', async (ctx, next) => {sendResponse(ctx, { items: cookies });next();})// 解析 post 字符串为 jsonfunction parsePostData(ctx) {return new Promise((resolve, reject) => {try {let postdata = '';ctx.req.addListener('data', (data) => {postdata += data;});ctx.req.on("end", function() {console.log('1111' + postdata);resolve(JSON.parse(postdata));})} catch (error) {reject(error);}});}//格式化JSON返回值function sendResponse(ctx, data={}, code = 0, message = 'SUCCESS') {const res = JSON.stringify({ data, code, message })ctx.response.body = res;}
处理静态资源(koa-static)
const KoaStatic = require('koa-static');const staticUtil = require('./static');app.use(KoaStatic('../dist'));//设置静态文件的路径app.use(async (ctx, next) => {console.log(ctx.url);if (ctx.url.startsWith('/api')) {next();return;}// 获取静态资源内容,有可能是文件内容,目录,或404let fullStaticPath = path.join(__dirname, '../dist')console.log(fullStaticPath);// 获取静态资源内容,有可能是文件内容,目录,或404let _content = await staticUtil.content(ctx, fullStaticPath)// 解析请求内容的类型let _mime = staticUtil.parseMime(ctx.url)// 如果有对应的文件类型,就配置上下文的类型if (_mime) {ctx.type = _mime}// 输出静态资源内容if (_mime && _mime.indexOf('image/') >= 0) {// 如果是图片,则用node原生res,输出二进制数据ctx.res.writeHead(200)ctx.res.write(_content, 'binary')ctx.res.end()} else {// 其他则输出文本ctx.body = _content}});
./static 文件
const path = require('path')const fs = require('fs')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': 'image/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-ms-wmv','xml': 'text/xml'}/*** 遍历读取目录内容(子目录,文件名)* @param {string} reqPath 请求资源的绝对路径* @return {array} 目录内容列表*/function walk(reqPath) {let files = fs.readdirSync(reqPath)let dirList = []let fileList = []for (let i = 0, len = files.length; i < len; i++) {let item = files[i]let itemArr = item.split('.')let itemMime = (itemArr.length > 1) ? itemArr[itemArr.length - 1] : 'undefined'if (typeof mimes[itemMime] === 'undefined') {dirList.push(files[i])} else {fileList.push(files[i])}}let result = dirList.concat(fileList)return result}/*** 封装目录内容* @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 data-index="${index}"><a href="${url === '/' ? '' : url}/${item}">${item}</a>`}html = `${html}</ul>`return html}/*** 读取文件方法* @param {string} 文件本地的绝对路径* @return {string|binary}*/function file(filePath) {let content = fs.readFileSync(filePath, { encoding: 'utf-8' }, 'binary')return content}/*** 获取静态资源内容* @param {object} ctx koa上下文* @param {string} 静态资源目录在本地的绝对路径* @return {string} 请求获取到的本地内容*/async function content(ctx, fullStaticPath) {// 封装请求资源的完绝对径let reqPath = path.join(fullStaticPath, ctx.url)// 判断请求路径是否为存在目录或者文件let exist = fs.existsSync(reqPath)// 返回请求内容, 默认为空let content = ''if (!exist) {// 如果请求路径不存在,返回404content = '请求路径不存在!'} else {// 判断访问地址是文件夹还是文件let stat = fs.statSync(reqPath)if (stat.isDirectory()) {// 如果为目录,则渲读取目录内容content = dir(ctx.url, reqPath)} else {// 如果请求为文件,则读取文件内容content = await file(reqPath)}}return content}// 解析资源类型function parseMime(url) {let extName = path.extname(url)extName = extName ? extName.slice(1) : 'unknown'return mimes[extName]}module.exports = {parseMime: parseMime,content: content}
