在现代 web 应用中,API 文档是必不可少的一部分。但是手动维护文档通常是一项繁琐且易错的工作。因此,自动生成 API 文档是一个值得探索的解决方案。
本文将介绍如何使用 Koa.js 实现自动生成 API 文档的功能,并提供一些示例代码,帮助你更好地理解如何实现。
Koa.js 简介
对于那些不熟悉 Koa.js 的读者,这里简单介绍一下该框架。它是一个 Node.js 的 web 框架,它的核心特点是中间件机制。Koa.js 中的每一个请求都会经过一系列的中间件函数,中间件函数可以修改请求和响应的对象,或者做一些其他的事情。这种设计使 Koa.js 很灵活,你可以通过添加和修改中间件来满足你的需求。
实现思路
在实现自动生成 API 文档之前,我们需要对 API 的定义方式做出一些规范。在本文中,我们会使用一种简单的注解方式。具体来说,我们会在每个 API 的实现函数上添加如下注解:
// javascriptcn.com 代码示例 /** * @api {METHOD} path * @apiName apiName * @apiDescription description * @apiVersion version * @apiGroup apiGroup * @apiParam {paramType} paramName paramDescription * @apiSuccess {successType} successName successDescription */
比如下面是一个示例:
// javascriptcn.com 代码示例 /** * @api {GET} /api/hello * @apiName hello * @apiDescription Say hello * @apiVersion 1.0.0 * @apiGroup hello * @apiParam {String} name Your name * @apiSuccess {String} message Hello, {name}! */ async function sayHello(ctx) { const name = ctx.query.name || 'World'; ctx.body = { message: `Hello, ${name}!` }; }
我们利用注解来描述每个 API 的基本信息,包括请求方法、路径、名称、描述、版本、分组、参数和成功响应等。每次新增或修改 API 时,只需要相应地添加或修改注解即可,无需手动修改文档。
然后,我们需要编写一个能解析这些注解的中间件。这个中间件会在每次请求之后对所有添加了注解的 API 进行解析,然后生成一个 API 列表。最后,我们可以在一个专门的接口上访问这个列表,从而实现自动生成 API 文档的功能。
接下来,我们会分步骤实现这个思路。
实现步骤
1. 解析注解
实现解析注解的函数需要以下几个步骤:
- 读取每个 API 的实现函数的源码
- 匹配源码中的注解,提取出每个 API 的基本信息
- 根据基本信息生成一个 API 对象
Koa.js 中的每个中间件函数接收两个参数:一个请求和一个响应对象。我们可以利用这个特性,在处理请求的同时,读取 API 的定义,并将解析出来的信息存到一个列表中。
解析注解需要进行一些复杂的字符串匹配,为了便于处理,这里我们使用了一个开源的库——JSDoc Parser。
具体实现如下:
// javascriptcn.com 代码示例 const jsdoc = require('jsdoc-api'); const _ = require('lodash'); async function parseAPI(fn) { const comments = await jsdoc.explain({ source: fn.toString() }); const apiComment = _.find(comments, { tags: [{ title: 'api' }] }); if (!apiComment) { return; } const api = _.pickBy( _.keyBy(apiComment.tags, 'title'), _.identity ); api.method = api.api[0]; api.path = api.api[1]; return api; }
这个函数接收一个 API 的实现函数,返回一个包含 API 信息的对象。这个对象利用了 lodash 的一些常用方法,比如 pickBy
和 keyBy
。
2. 自动生成 API 列表
在上一步中,我们已经得到了所有 API 的信息。但是,这些信息只是散落在每个实现函数的注解中,并无有效的结构。
为了生成 API 列表,我们需要将这些信息整合起来,并按照分组进行归类。接下来的代码展示了如何将所有 API 信息转化为一个分组 API 列表:
// javascriptcn.com 代码示例 async function generateAPIDocs() { const allRoutes = app._router.stack.filter((stack) => stack.route ? true : false); const apis = []; for (const route of allRoutes) { const api = await parseAPI(route.route.stack[0].handle); if (api) { api.path = route.route.path; apis.push(api); } } const grouped = _.groupBy(apis, 'group'); const all = { name: '所有接口', children: apis }; const groups = _.map(grouped, (apis, name) => ({ name, children: apis })); return [all, ...groups]; }
这个函数通过遍历 Koa.js 应用的路由栈,获取每个 API 的信息,并将其添加到一个数组中。利用 lodash 的 groupBy
函数,我们对所有 API 进行分组,然后生成一个分组列表。
最后,我们将所有 API 添加到一个名为“所有接口”的分组中,作为一个备选方案。这样就可以在 API 列表中快速访问所有接口了。
3. 提供 API 列表
我们将分组 API 列表作为一个 JSON 返回给客户端。Koa.js 中的中间件函数通常会返回一个 JSON 格式的响应,比如:
async function (ctx, next) { ctx.body = { message: 'Hello world!' }; }
这里使用了 Koa.js 中的 ctx
上下文对象。具体来说,我们可以编写一个中间件函数,用于处理 API 列表请求,代码如下:
app.use(async (ctx, next) => { if (ctx.path === '/api-docs') { ctx.body = await generateAPIDocs(); } else { await next(); } });
这个中间件函数会对包含“/api-docs”的请求进行响应,返回 API 列表。对于其他请求,它会继续进行处理,直到找到一个能够处理该请求的中间件。
完整示例代码
// javascriptcn.com 代码示例 const Koa = require('koa'); const koaBody = require('koa-body'); const Router = require('koa-router'); const jsdoc = require('jsdoc-api'); const _ = require('lodash'); const app = new Koa(); const router = new Router(); router.get('/api/hello', async (ctx, next) => { /** * @api {GET} /api/hello * @apiName hello * @apiDescription Say hello * @apiVersion 1.0.0 * @apiGroup hello * @apiParam {String} name Your name * @apiSuccess {String} message Your message */ const name = ctx.query.name || 'World'; ctx.body = { message: `Hello, ${name}!` }; }); async function parseAPI(fn) { const comments = await jsdoc.explain({ source: fn.toString() }); const apiComment = _.find(comments, { tags: [{ title: 'api' }] }); if (!apiComment) { return; } const api = _.pickBy( _.keyBy(apiComment.tags, 'title'), _.identity ); api.method = api.api[0]; api.path = api.api[1]; return api; } async function generateAPIDocs() { const allRoutes = app._router.stack.filter((stack) => stack.route ? true : false); const apis = []; for (const route of allRoutes) { const api = await parseAPI(route.route.stack[0].handle); if (api) { api.path = route.route.path; apis.push(api); } } const grouped = _.groupBy(apis, 'group'); const all = { name: '所有接口', children: apis }; const groups = _.map(grouped, (apis, name) => ({ name, children: apis })); return [all, ...groups]; } app.use(async (ctx, next) => { if (ctx.path === '/api-docs') { ctx.body = await generateAPIDocs(); } else { await next(); } }); app.use(koaBody()); app.use(router.routes()).use(router.allowedMethods()); app.listen(3000, () => { console.log('Server is listening on http://localhost:3000'); });
现在你可以启动这个应用,并访问 /api-docs
接口,以获得自动生成的 API 文档信息。
总结
在本文中,我们介绍了如何使用 Koa.js 框架实现自动生成 API 文档的功能。其中,我们介绍了利用注解进行 API 定义的方法,并实现了解析注解和生成 API 列表的功能。
通过这个项目,你可以学习到如何使用 Koa.js 的中间件机制和注解语法。同时,这个项目也给前端开发者提供了一个自动生成 API 文档的方案,可以极大地提高工作效率,减少出错的可能。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652b4e7a7d4982a6ebd47507