本来在网上想找一个 koa 的基于 swagger 的验证框架,试了几个不是太老,就是 bug 太多,不仅不能满足需求,而且扩展性也很差。在给 swagger2 (swagger2-koa) 提了个 pr之后,还是放弃了 …
开始想用 ajv 来折腾一个,但是发现它缺乏成熟的方案满足我的需求
- 自动删除多余字段
- 自动转换各种格式类型
- 写起来不要太冗长 (JSON schema 躺枪)
JavaScript 中最强大的验证框架莫过于 joi了,丰富而简洁的 API,良好的文档,足够的扩展性,足以满足绝大多数的验证需求。
其中还纠结了半天用 joi 来验证 JSON schema 的 enjoi,不过感觉转来转去似乎坑更多,于是最终还是选择了 手写 joi,使用 joi 验证,joi schema 转 swagger使用 joi-to-json-schema文档的方案最终的成果便是一个全新的 koa swagger 验证库诞生了:
koa-joi-swagger
使用方法
app.js
import { toSwaggerDoc, ui, mixedValidate } from '../../src'
import mixedDoc from './mixed-doc'
import Koa from 'koa'
import DecRouter from 'koa-dec-router'
import bodyparser from 'koa-bodyparser'
const app = new Koa()
这里使用 koa-dec-router 做路由,支持 oo + decorators + 自动挂载 的路由
const decRouter = DecRouter({
controllersDir: `${__dirname}/controllers`,
})
app.use(bodyparser())
const swaggerDoc = toSwaggerDoc(mixedDoc)
// 将 swagger ui 挂载到 `/swagger`
app.use(ui(swaggerDoc, {pathRoot: '/swagger'}))
// handle validation errors
app.use(async (ctx, next) => {
try {
await next()
} catch (e) {
if (e.name === 'RequestValidationError') {
ctx.status = 400
ctx.body = { code: 1, message: e.message, data: e.data }
} else if (e.name === 'ResponseValidationError') {
ctx.status = 500
ctx.body = { code: 1, message: e.message, data: e.data }
}
}
})
// 使用 mixedDoc 来验证 request 和 response
app.use(mixedValidate(mixedDoc, {
onError: e => console.log(e.details, e._object),
}))
// koa-dec-router
app.use(decRouter.router.routes())
app.use(decRouter.router.allowedMethods())
app.listen(3456)
mixed-doc.js joi + swagger 混合文档, 基本就是 swagger 文档,只不是原来是 JSON schema 的 parameters 和 responses 全都替换成了 joi schema。
export default {
swagger: '2.0',
info: {
title: 'Test API',
description: 'Test API',
version: '1.0.0',
},
// the domain of the service
// host: 127.0.0.1:3457
// array of all schemes that your API supports
schemes: ['https', 'http'],
// will be prefixed to all paths
basePath: '/api/v1',
consumes: ['application/x-www-form-urlencoded'],
produces: ['application/json'],
paths: {
'/posts': {
get: {
summary: 'Some posts',
tags: ['Post'],
parameters: {
query: Joi.object().keys({
type: Joi.string().valid(['news', 'article']),
}),
},
responses: {
'200': {
description: 'Post list',
schema: Joi.object().keys({
lists: Joi.array().items(Joi.object().keys({
title: Joi.string().description('Post title'),
content: Joi.string().required().description('Post content'),
}))
}),
},
'default': {
description: 'Error happened',
schema: Joi.object().json().keys({
code: Joi.number().integer(),
message: Joi.string(),
data: Joi.object(),
}),
},
}
}
},
},
}
优势
- 直接用 js 写文档,方便复用,可以对所有的最终 schema 包装一层函数,比如:
function api(schema = Joi.object()) {
return Joi.object().keys({
code: Joi.number().integer(),
data: schema,
})
}
// mixed doc
// ...
responses: {
'200': {
description: 'Some data',
schema: api(Joi.object().keys({
title: Joi.string(),
content: Joi.string(),
}))
}
2. 简单可靠,手写的 joi schema 直接拿来验证,不用担心 swagger 是否冲突,开发环境不会干扰生产环境 3. 无侵入性,验证直接挂载在全局的中间件中,无论是验证还是文档都不会侵入已有的代码,可以渐进添加。
希望大家能喜欢~ 🤓