Nodejs框架Koa常用的一些中间件

作者: 贺鹏飞 分类: Node.Js 发布时间: 2021-01-29 16:09

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

着ES6的普及,async/await的语法受到更多JS开发者的青睐,Koa.js作为比较早支持使用该语法的Node框架越来越受到大家的喜爱,虽然Koa.js本身支持的功能很有限,但官方和社区提供了很多各种功能的中间件,本文精选常用的中间件,对于我们开发应用程序或者框架将会特别有用。

koa-router 详情

路由是Web框架必不可少的基础功能,koa.js为了保持自身的精简,并没有像Express.js自带了路由功能,因此koa-router做了很好的补充,作为koa星数最多的中间件,koa-router提供了全面的路由功能,比如类似Expressapp.get/app.post/app.put的写法,URL命名参数、路由命名、支持加载多个中间件、嵌套路由等。其他可选路由中间件:koa-routekoa-joi-routerkoa-trie-router

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
 
//实现 '/'、'/koa'两个路由层级
router
    .get('/',(ctx,next)=>{
        ctx.body="Index page";
    })
    .post('/koa',(ctx,next)=>{
        ctx.body="Koa page";
    });
app
  .use(router.routes())
  .use(router.allowedMethods());
  
app.listen(3000,()=>{
    console.log('端口号是 3000');
});

koa-views 详情

koa-views对需要进行视图模板渲染的应用是个不可缺少的中间件,支持ejsnunjucks等众多模板引擎。

const views = require('koa-views')
const path = require('path')
// 配置视图
app.use(views(path.join(__dirname, './views'), {
    extension: 'ejs'
}))

koa-bodyparser 详情

通过post来传递表单、json或上传文件,数据不容易获取,通过koa-bodyparser解析之后,在koa中this.body就能获取到数据。koa.js并没有内置Request Body的解析器,当我们需要解析请求体时需要加载额外的中间件,官方提供的koa-bodyparser是个很不错的选择,支持x-www-form-urlencoded, application/json等格式的请求体,但不支持form-data的请求体,需要借助 formidable 这个库,也可以直接使用 koa-body 或 koa-better-body

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
app.use(bodyParser());

app.use(async ctx => {
  //可以获取到request.body
  ctx.body = ctx.request.body;
});

koa-static 详情

Node.js除了处理动态请求,也可以用作类似Nginx的静态文件服务,在本地开发时特别方便,可用于加载前端文件或后端Fake数据,可结合 koa-compress 和 koa-mount 使用。

const Koa = require('koa')
const static = require('koa-static')
const app = new Koa()
const staticPath = './static'

//即可直接通过'./static',访问到静态资源
app.use(static(
  path.join(__dirname, staticPath)
))

app.listen(3000, () => {
  console.log('端口号是 3000')
})

koa-session 详情

HTTP是无状态协议,为了保持用户状态,我们一般使用Session会话,koa-session提供了这样的功能,既支持将会话信息存储在本地Cookie,也支持存储在如RedisMongoDB这样的外部存储设备。

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const session=require('koa-session');
const app = new Koa();
app.keys = ['this is my secret and fuck you all'];//我理解为一个加密的密钥

app.use(session({
    key: 'koa:sess', //cookie key (default is koa:sess) 默认
    maxAge: 5000, // cookie 的过期时间 maxAge in ms (default is 1 days)             【需要修改】
    overwrite: true, //是否可以 overwrite (默认 default true) 
    httpOnly: true, //cookie 是否只有服务器端可以访问 httpOnly or not (default true)
    signed: true, //签名默认 true
    rolling: false, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false) 【需要修改 跟下面的二选一】
    renew: true, //等快要到期时重置 ☆前提是你此次请求的session还没有过期 然后在发请求的时候会给你重置为新的  【需要修改】
},app));

app.use(bodyParser());

koa-jwt 详情

随着网站前后端分离方案的流行,越来越多的网站从Session Base转为使用Token BaseJWT(Json Web Tokens)作为一个开放的标准被很多网站采用,koa-jwt这个中间件使用JWT认证HTTP请求。

const koa = require('koa');
const koajwt = require('koa-jwt');
const jsonwebtoken = require('jsonwebtoken');
const util = require('util');
const koabody = require('koa-body');
const app = new koa();

const SECRET = 'shared-secret'; // demo,可更换
app.use(koabody());
// 中间件对token进行验证
app.use(async (ctx, next) => {
    // let token = ctx.header.authorization;
    // let payload = await util.promisify(jsonwebtoken.verify)(token.split(' ')[1], SECRET);
    return next().catch((err) => {
        if (err.status === 401) {
            ctx.status = 401;
            ctx.body = {
                code: 401,
                msg: err.message
            }
        } else {
            throw err;
        }
    })
});

app.use(koajwt({ secret: SECRET }).unless({
    // 登录接口不需要验证
    path: [/^\/api\/login/]
}));

// 示例
const USER = {
    username: 'zhangsan',
    password: '123456',
    id: 100
}
// 登录接口签发token, 为了简便不使用router
app.use(async (ctx, next) => {
    if (ctx.path === '/api/login' && ctx.method === 'POST') {
        // 登录
        // 判断用户名密码是否匹配
        let checkUser = ctx.request.body.username == USER.username && ctx.request.body.password == USER.password;
        if (checkUser) {
            ctx.body = {
                code: 200,
                msg: '登录成功',
                token: jsonwebtoken.sign(
                    { name: USER.username, id: USER.id },  // 加密userToken
                    SECRET,
                    { expiresIn: '1h' }
                )
            }
        } else {
            // 登录失败, 用户名密码不正确
            ctx.body = {
                code: 400,
                msg: '用户名密码不匹配'
            }
        }
    } else if (ctx.path === '/api/user' && ctx.method === 'GET') {
        // 获取用户信息
        // 中间件统一验证token
        ctx.body = {
            code: 200,
            data: USER,
            msg: '请求成功'
        }
    }
})
app.listen(3000,()=>{
    console.log('端口号是 3000');
});

koa-multer 详情

上传文件在开发中是很常见的操作,今天我选择使用koa-multer中间件来实现这一功能,除了上传文件外,我还会对文件上传进行限制,以及发生上传错误时的处理。

由于原来的 koa-multer 已经停止维护,我们要使用最新的 @koa/multer 。这个模块是 koa-multer 的一个分支,它被分叉到官方的Koa组织中,并以@koa/multer包名提供。

@koa/multer 依赖于 multer,安装时要将 multer 一并安装上,安装命令如下

 npm install –save @koa/multer multer

const Koa = require('koa')
const Router = require('koa-router')
const route = new Router()
const multer = require('@koa/multer')
const path = require('path')

//上传文件存放路径、及文件命名
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, path.join(__dirname ,'/public'))
    },
    filename: function (req, file, cb) {
        let type = file.originalname.split('.')[1]
        cb(null, `${file.fieldname}-${Date.now().toString(16)}.${type}`)
    }
})
//文件上传限制
const limits = {
    fields: 10,//非文件字段的数量
    fileSize: 500 * 1024,//文件大小 单位 b
    files: 1//文件数量
}
const upload = multer({storage,limits})
route.post('/user/file', upload.single('file'), async (ctx,next)=>{
    ctx.body = {
        code: 1,
        data: ctx.file
    }
})
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)

koa-helmet 详情

网络安全得到越来越多的重视,helmet 通过增加如Strict-Transport-SecurityX-Frame-OptionsX-Frame-OptionsHTTP头提高Express应用程序的安全性,koa-helmetkoa程序提供了类似的功能,参考Node.js安全清单。

// This...
app.use(helmet());
// ...is equivalent to this:
app.use(helmet.contentSecurityPolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());
const Koa = require("koa");
const helmet = require("koa-helmet");
const app = new Koa();

app.use(helmet());
app.use((ctx) => {
  ctx.body = "Hello World"
});
app.listen(4000);

koa-compress 详情

当响应体比较大时,我们一般会启用类似Gzip的压缩技术减少传输内容,koa-compress提供了这样的功能,可根据需要进行灵活的配置。

const Koa = require('koa');
const app = new Koa();
const compress = require('koa-compress');
 
// compress data
app.use(compress({
    filter: function (content_type) { //只有在请求的content-type中有gzip类型,我们才会考虑压缩,因为zlib是压缩成gzip类型的
        return /text/i.test(content_type);
    },
    threshold: 2048, //阀值,当数据超过2kb的时候,可以压缩
    flush: require('zlib').Z_SYNC_FLUSH
}));
 
//使用
app.use(async(ctx, next) => {
    ctx.compress = true; //是否压缩数据
    await next();
});

koa-logger 详情

koa-logger提供了输出请求日志的功能,包括请求的url、状态码、响应时间、响应体大小等信息,对于调试和跟踪应用程序特别有帮助,koa-bunyan-logger 提供了更丰富的功能。

//koa-logger.js
module.exports = async(ctx,next)=>{
    const start = new Date().getTime()
    // 中间件异步处理
    await next()
    const end = new Date().getTime()
    // 打印出耗时还有长度
    console.log(ctx.request.url,end-start,ctx.body.length)
}
//server.js
const Koa = require('koa')
const app = new Koa()
const koaLog = require('koa-logger')

function delay(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve()
        },1000)
    })
}
app.use(koaLog)
app.use(async(ctx,next)=>{
    ctx.body = '1'
    //下一个中间件
    await next()
    ctx.body = ctx.body + '2'
})
app.use(async(ctx,next)=>{
    ctx.body+= '3'
    //下一个中间件
    await next()
    ctx.body = ctx.body + '4'
})
app.use(async(ctx,next)=>{
    ctx.body += '5'
    await delay()
    //下一个中间件
    await next()
    ctx.body = ctx.body + '6'
})
//启动应用
app.listen('3000')

koa2-cors 详情

koa2-cors让后台允许跨域直接就可以在客户端使用ajax请求数据。

const Koa = require('koa');
const bodyParser = require('koa-bodyparser'); //post数据处理
const router = require('koa-router')(); //路由模块
const cors = require('koa2-cors'); //跨域处理
const app = new Koa();
app.use(
    cors({
        origin: function(ctx) { //设置允许来自指定域名请求
            if (ctx.url === '/test') {
                return '*'; // 允许来自所有域名请求
            }
            return 'http://localhost:8080'; //只允许http://localhost:8080这个域名的请求
        },
        maxAge: 5, //指定本次预检请求的有效期,单位为秒。
        credentials: true, //是否允许发送Cookie
        allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //设置所允许的HTTP请求方法
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段
    })
);
router.post('/', async function (ctx) {
    ctx.body = '请求成功了'
});

app.listen(3000);

koa-jsonp 详情

const Koa = require('koa')
const jsonp = require('koa-jsonp')
const app = new Koa()

// 使用中间件  
app.use(jsonp())
app.use( async ( ctx ) => {
  let returnData = {
    success: true,
    data: {
      text: 'this is a jsonp api',
      time: new Date().getTime(),
    }
  }
  // 直接输出JSON支持jsonp
  ctx.body = returnData
})

koa-onerror 详情

const onerror = require('koa-onerror')
// error handler
onerror(app)
app.on('error', (err, ctx) => {
  console.error('server', err, ctx)
})
//修改app.js,修改404设置,设置404显示页面
app.use(async (ctx, next) => {
  await next()
  if (ctx.status == 404) {
    ctx.status = 404;
    await ctx.render('404', {})
  }
})

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

发表回复

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