最近因为开发一个自己的博客网站,学习了koa2的搭建,写了一些自己认为比较重要或需要知道的koa2中间件作用和使用场景。koa-router
koa-router koa-router 路由中间件下载

npm i koa-router
npm i koa-router 使用
使用使用
普通使用方法
普通使用方法需要注意的是引入的koa-router是一个方法,引入后需要执行这个方法。
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();

// 配置路由url
// 默认url
router.get('/', async (ctx, next) => {
ctx.body = 'Hello World';
});

// 自定义url
router.get('/hello/:name', async (ctx, next) => {
var name = ctx.params.name;
ctx.response.body = \`

Hello, ${name}!

\`;
});

// 注册路由
app.use(router.routes(), router.allowedMethods());
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();

// 配置路由url
// 默认url
router.get('/', async (ctx, next) => {
ctx.body = 'Hello World';
});

// 自定义url
router.get('/hello/:name', async (ctx, next) => {
var name = ctx.params.name;
ctx.response.body = \`

Hello, ${name}!

\`;
});

// 注册路由
app.use(router.routes(), router.allowedMethods()); 也可以按需引入并注册路由,可注册多个路由。不同请求下接收的参数获取
不同请求下接收的参数获取router.get
通过ctx.query获取参数router.post
通过ctx.request.body获取参数动态路由 router.get('/:id', func)
通过ctx.params获取参数遍历注册router
遍历注册router
首先引入nodejs中的fs模块,使用fs的readdirSync方法获取到指定目录的所有文件名,遍历引入路由模块并注册。const fs = require('fs'); 
// fs.readdirSync 获取指定目录下的所有文件名称,遍历引入路由模块并注册
fs.readdirSync('./routes').forEach(route=> {
let api = require(\`./routes/${route}\`);
app.use(api.routes(), api.allowedMethods());
});

// fs.readdirSync 获取指定目录下的所有文件名称,遍历引入路由模块并注册
fs.readdirSync('./routes').forEach(route=> {
let api = require(\`./routes/${route}\`);
app.use(api.routes(), api.allowedMethods());
});
koa-router中的其它api
koa-router中的其它apirouter.prefix(prefix) 添加url前缀
设置已经初始化的路由器实例的路径前缀
router.prefix('/user');
router.post('/login', function(ctx, next) {
...
});

// 实际路径 /user/login


router.prefix('/user');
router.post('/login', function(ctx, next) {
...
});

// 实际路径 /user/login

router.use(url|[url1,url2,...], (ctx, next) => {...}) 路由中间件
router.use(url|[url1,url2,...], (ctx, next) => {...}) 路由中间件使用场景:我们通常需要通过验证用户和用户权限来判定用户是否能使用该接口,如果在每个接口都写一次验证非常麻烦且不好维护。这时我们就需要路由中间件先进行验证,再执行下面操作。router.use的第一个参数为一个路径或者由多个需要使用中间件的路径组成的数组(需要注意的是,如果路由设置了url前缀,需要在设置前缀后注册中间件,参数中的url不需要设置前缀)。router.use的第二个参数为函数,函数传递ctx和next两个参数,可通过ctx进行权限验证后,判断是否执行next调用接口。这里特别需要注意的是函数使用next的时候需要添加await,如果不添加,在调用接口前,接口就会返回结果,前台则无法获取到数据。
// this is wrong
app.use(function (ctx, next) {

ctx.set("Access-Control-Allow-Origin", "\*");

next();
});
// this is right
app.use(async function (ctx, next) {

ctx.set("Access-Control-Allow-Origin", "\*");

await next();
});

// this is wrong
app.use(function (ctx, next) {

ctx.set("Access-Control-Allow-Origin", "\*");

next();
});
// this is right
app.use(async function (ctx, next) {

ctx.set("Access-Control-Allow-Origin", "\*");

await next();
});
koa-bodyparser处理post请求
koa-bodyparser处理post请求
处理post请求时,我们会遇到一个问题,无论是node的request对象还是koa的request对象,都没办法解析request的body,我们就需要下载并引入一个解析body的中间件koa-bodyparser,koa-bodyparser的具体配置详见下面的说明,这里我们直接放入使用方式。
// 引入路由文件
const index = require('./routes/index.js');
const user = require('./routes/user.js');

// 引入解析request.body的中间件
const bodyparser = require('koa-bodyparser');

// 注册bodyparser,需要注意,bodyparser的注册一定要在router路由注册之前
app.use(bodyparser({

enableTypes:\['json', 'form', 'text'\]
}));

...

// 注册routes
app.use(index.routes(), index.allowedMethods());
app.use(user.routes(), user.allowedMethods());


// 引入路由文件
const index = require('./routes/index.js');
const user = require('./routes/user.js');

// 引入解析request.body的中间件
const bodyparser = require('koa-bodyparser');

// 注册bodyparser,需要注意,bodyparser的注册一定要在router路由注册之前
app.use(bodyparser({

enableTypes:\['json', 'form', 'text'\]
}));

...

// 注册routes
app.use(index.routes(), index.allowedMethods());
app.use(user.routes(), user.allowedMethods());

koa-bodyparser
koa-bodyparser如上所述,koa-bodyparser用于解析request.body,因为node和koa的request无法解析body。下载
npm i koa-bodyparser
npm i koa-bodyparser 使用
const bodyparser = require('koa-bodyparser');
const bodyparser = require('koa-bodyparser');在注册运行时,bodyparser方法中可传入对象,作相应配置。- enableTypes:解析器只在配置了enableTypes时解析请求类型,默认是['json', 'form']。
- encoding:请求编码,默认是utf-8。
- formLimit:urlencoded body的imit如果主体最终大于此限制,则返回一个413错误代码。默认是56 kb。
- jsonLimit:json主体的限制。默认是1 mb。
- textLimit:文本主体的限制。默认是1 mb。
- strict:当设置为true时,JSON解析器将只接受数组和对象。默认是正确的。参见正文中的严格模式。在严格模式下,ctx.request。body总是一个对象(或数组),这避免了很多类型判断。但文本正文总是返回字符串类型。
- detectJSON:自定义json请求检测函数。默认为null。
app.use(bodyparser({
detectJSON: function (ctx) {
return /\\.json$/i.test(ctx.path);
}
}));


app.use(bodyparser({
detectJSON: function (ctx) {
return /\\.json$/i.test(ctx.path);
}
}));

- extendTypes:支持扩展类型

app.use(bodyparser({

extendTypes: {

json: \['application/x-javascript'\] // 解析application/x-javascript 类型 作为JSON字符串

}

}));


app.use(bodyparser({

extendTypes: {

json: \['application/x-javascript'\] // 解析application/x-javascript 类型 作为JSON字符串

}

}));
- onerror:支持自定义错误句柄,如果koa-bodyparser抛出一个错误,您可以自定义响应如下:

app.use(bodyparser({

onerror: function (err, ctx) {

ctx.throw('body parse error', 422);

}

}));



app.use(bodyparser({

onerror: function (err, ctx) {

ctx.throw('body parse error', 422);

}

}));

- disableBodyParser:可以通过设置ctx动态禁用body解析器。disableBodyParser = true。

app.use(async (ctx, next) => {

if (ctx.path === '/disable')
ctx.disableBodyParser = true;

await next();

});

app.use(bodyparser());


app.use(async (ctx, next) => {

if (ctx.path === '/disable')
ctx.disableBodyParser = true;

await next();

});

app.use(bodyparser());
koa-logger
koa-loggerkoa-logger请求响应监听日志下载

npm i koa-logger
npm i koa-logger 使用

~~// 引入日志中间件
const logger = require('koa-logger');

// 注册日志中间件
app.use(logger()); ~~


~~// 引入日志中间件
const logger = require('koa-logger');

// 注册日志中间件
app.use(logger()); ~~

koa-session
koa-sessionkoa-session用于Koa的简单会话中间件。默认为基于cookie的会话,并支持外部存储。session
session我们知道,http协议是无状态的,当用户进行登录后,并不会保存账户密码,如果我们需要维持用户的登录状态,就需要使用一些方法。目前主流的用户认证方法有基于token和基于session两种方式。基于token的认证可以使用koa-jwt中间件,基于session的认证则使用标题的koa-session。下载下载
npm i koa-session
npm i koa-session使用
使用app.js 入口文件中注册session

const CONFIG = {
key: 'koa:sess', /\*\* (string) cookie key (default is koa:sess) \*/
/\*\* (number || 'session') maxAge in ms (default is 1 days) \*/

// 状态保存最大时间,默认为一天
maxAge: 86400000,
autoCommit: true, /\*\* (boolean) 自动保存头部 (default true) \*/
overwrite: true, /\*\* (boolean) 能否覆盖 (default true) \*/
httpOnly: true, /\*\* (boolean) httpOnly or not (default true) \*/
signed: true, /\*\* (boolean) signed or not (default true) \*/
/\*\* (boolean) 强制在每个响应上设置会话标识符cookie。过期将重置为原始maxAge,重新设置过期倒计时。 (default is false) \*/
rolling: false,
/\*\* (boolean) 当会话快过期时续订会话,这样我们可以始终保持用户登录。(default is false)\*/
renew: false,
};

// 如果你所有都为默认配置,则不需要传递配置参数,app.use(session(app))即可
app.use(session(sessionConfig, app));


const CONFIG = {
key: 'koa:sess', /\*\* (string) cookie key (default is koa:sess) \*/
/\*\* (number || 'session') maxAge in ms (default is 1 days) \*/

// 状态保存最大时间,默认为一天
maxAge: 86400000,
autoCommit: true, /\*\* (boolean) 自动保存头部 (default true) \*/
overwrite: true, /\*\* (boolean) 能否覆盖 (default true) \*/
httpOnly: true, /\*\* (boolean) httpOnly or not (default true) \*/
signed: true, /\*\* (boolean) signed or not (default true) \*/
/\*\* (boolean) 强制在每个响应上设置会话标识符cookie。过期将重置为原始maxAge,重新设置过期倒计时。 (default is false) \*/
rolling: false,
/\*\* (boolean) 当会话快过期时续订会话,这样我们可以始终保持用户登录。(default is false)\*/
renew: false,
};

// 如果你所有都为默认配置,则不需要传递配置参数,app.use(session(app))即可
app.use(session(sessionConfig, app));

session的使用
session的使用注册后可以通过上下文ctx找到session属性,下面代码将用户信息存入session属性中,并创建key和valueuser.js
// 用户登录
static async login(ctx, next) {

const data = ctx.request.body
const schema = Joi.object().keys({
username: Joi.string().required(),
password: Joi.string().required(),
})
let result = Joi.validate(data, schema)
// 如果有字段类型错误
if(result.error) {
return ctx.response.body = { status: 400, msg: result.error.details};
}
let { username, password } = result.value // 验证过后的数据
// 查找是否有该用户
const userInfo = await usersModel.findByName(username)
// 如果有该用户,继续验证密码是否正确
if (userInfo) {
const userInfo2 = await usersModel.findOne({ username, password })
if (userInfo2) {

ctx.response.body = {state: 200, msg: '登录成功'}

// 设置session

ctx.session.userInfo = userInfo2
} else {

ctx.response.body = {state: 410, msg: '密码错误'}
}
// 如果没有该用户,返回结果
} else {
ctx.response.body = {state: 410, msg: '该用户不存在'}
}
return ctx.response.body
}


// 用户登录
static async login(ctx, next) {

const data = ctx.request.body
const schema = Joi.object().keys({
username: Joi.string().required(),
password: Joi.string().required(),
})
let result = Joi.validate(data, schema)
// 如果有字段类型错误
if(result.error) {
return ctx.response.body = { status: 400, msg: result.error.details};
}
let { username, password } = result.value // 验证过后的数据
// 查找是否有该用户
const userInfo = await usersModel.findByName(username)
// 如果有该用户,继续验证密码是否正确
if (userInfo) {
const userInfo2 = await usersModel.findOne({ username, password })
if (userInfo2) {

ctx.response.body = {state: 200, msg: '登录成功'}

// 设置session

ctx.session.userInfo = userInfo2
} else {

ctx.response.body = {state: 410, msg: '密码错误'}
}
// 如果没有该用户,返回结果
} else {
ctx.response.body = {state: 410, msg: '该用户不存在'}
}
return ctx.response.body
}

koa-router路由中间件中验证session,这里需要注意的是next()方法前一定要使用await,不然程序不会等待next()方法执行。
export const verifyUser = async (ctx, next) => {

if (ctx.session.userInfo) {
await next()
} else {
return ctx.response.body = { state: 401, msg: '无权限访问' }
}
}


export const verifyUser = async (ctx, next) => {

if (ctx.session.userInfo) {
await next()
} else {
return ctx.response.body = { state: 401, msg: '无权限访问' }
}
}

koa2-cors
koa2-cors
koa2-cors用于解决跨域问题下载

npm install koa2-cors
npm install koa2-cors使用

app.use(cors())
app.use(cors())