Nodejs框架Koa常用的一些中间件
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
提供了全面的路由功能,比如类似Express
的app.get
/app.post
/app.put
的写法,URL
命名参数、路由命名、支持加载多个中间件、嵌套路由等。其他可选路由中间件:koa-route
, koa-joi-router
, koa-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
对需要进行视图模板渲染的应用是个不可缺少的中间件,支持ejs
, nunjucks
等众多模板引擎。
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
,也支持存储在如Redis
, MongoDB
这样的外部存储设备。
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 Base
,JWT
(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-Security
, X-Frame-Options
, X-Frame-Options
等HTTP
头提高Express
应用程序的安全性,koa-helmet
为koa
程序提供了类似的功能,参考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', {})
}
})