smart-koa-router

All in one router for koa.

Usage no npm install needed!

<script type="module">
  import smartKoaRouter from 'https://cdn.skypack.dev/smart-koa-router';
</script>

README

smart-koa-router

概述

smart-koa-router是一个复合型的Koa路由处理模块,支持Restful API、静态文件、文件上传、模板渲染,自动解析body参数,支持允许跨域请求,支持API参数校验和自动生成文档,支持应用启动时拦截等功能。

这是一个与koa-router在使用方式上接近,但是设计理念完全不同的Koa路由处理模块:不仅覆盖了web应用需要的所有场景,而且更加灵活方便,在性能上也比koa-router表现更为优异。

如何使用

Router是一个class,且使用了asyncawait,因此需要在node v7.6+环境中才能使用。

实例化

你可以根据需要多次实例化Router对象,每个实例化router对象使用不同的配置。

var Router = require('smart-koa-router');
var router = new Router();

router.get('/index', function(ctx) {
    ctx.body = 'Hello world!';
});

所有的配置参数都是可选的,这时会使用默认值或者具体某个路由的设置。

  • options.prefix: 路由前缀,可通过router.prefix()方法覆盖,默认为/
  • options.methods: 支持的methods,默认为['HEAD', 'OPTIONS', 'GET', 'PUT', 'PATCH', 'POST', 'DELETE']
  • options.parseBody: 默认情况下,对于POSTPUTPATCH请求,会自动解析body,也可以自定义解析body配置
  • options.allowCross: 是否允许跨域,默认为false,可以简单地设置为true来使用默认配置,也可以自定义跨域配置
  • options.staticFile: 默认的静态文件资源配置
  • options.uploadFile: 默认的文件上传配置
  • options.renderFile: 默认的模板渲染配置
  • options.swagger: 默认的Swagger文档配置
  • options.validator: 参数校验配置,默认为true,可以简单地设置为false来禁用参数校验
  • options.document: swagger文档基础配置,具体可以参考OpenAPI Specification

使用路由

路由的实例化虽然是多次的,但是路由的使用却只需要一次。

var koa = require('koa');
var Router = require('smart-koa-router');

app.use(router.routes());

app.listen(8444, '0.0.0.0', function() {
    console.log('http://localhost:8444');
});

API

router.use(methods, path, opts, ...middleware)

定义一个路由拦截行为,统一拦截请求进入时的操作。

router.use(['POST', 'PUT', 'PATCH', 'DELETE'], async function authorizeMiddleware(ctx, next) {
    // 所有的增删改操作都要进入权限认证
    await next();
});

参数

  • methods: String|String[],接收的request method,如果省略则接收所有的默认允许的methods

  • path: String|RegExp,接收的request path,如果省略则等同于'*',如果为字符串则表示支持参数

    为了支持swagger的path参数格式,在解析参数时使用了open-path模块来代替path-to-regexp

  • opts: 路由配置,与实例化时的参数配置相同,该参数可以省略,也可以写在middleware的后面

  • middleware: 一个或多个路由处理中间件

router.param([path], param, middleware)

对指定路径参数作统一拦截,属于router.use()方法的语法糖。

router
    .param('user', (id, ctx, next) => {
        ctx.user = users[id];
        if (!ctx.user) return ctx.status = 404;
        return next();
    })
    .get('/users/:user', ctx => {
        ctx.body = ctx.user;
    })
    .get('/users/:user/friends', async (ctx) => {
        var friends = await ctx.user.getFriends();
        ctx.body = friends;
    });
    // /users/3 => {"id": 3, "name": "Alex"}
    // /users/3/friends => [{"id": 4, "name": "TJ"}]

router.prefix(prefix)

定义路由前缀,在此之后定义的路由,只要path是以./开头的,都会自动加上此前缀。

router.any(methods, path, opts, ...middleware)

不同于路由拦截,路由定义最多只会命中一个定义的路由,且后定义的路由将覆盖先定义的路由。

参数

相比于通用配置,多了一个opts.annotations配置,用于描述该路由和校验请求参数,声明方式请参考operation-object

另外还有一个opts.routeOptions配置,供中间件调用。

router.get(path, opts, ...middleware)

定义一个get请求,同时会定义一个head请求,如果需要自定义head请求,只需要手动定义一个head请求覆盖掉即可。

router.post(path, opts, ...middleware)

定义一个post请求。

router.put(path, opts, ...middleware)

定义一个put请求。

router.patch(path, opts, ...middleware)

定义一个patch请求。

router.delete(path, opts, ...middleware)

定义一个delete请求。

router.head(path, opts, ...middleware)

定义一个head请求。

router.options(path, opts, ...middleware)

定义一个options请求。

router.static(methods, path, opts, ...middleware)

通过定制的koa-send模块,满足提供静态文件服务的需求。

// 单个文件
router.static('/index.html', {
    staticFile: {
        filename: 'index.html',
        root: __dirname
    }
});

// 整个目录
router.static('/assets', {
    staticFile: {
        root: __dirname,
        include: /(css|js|html)$/i
    }
});

参数

除了methods的默认值为GET外,其余参数无区别。

router.render(methods, path, opts, ...middleware)

用于提供根据模板渲染页面的能力。

router.render(docUrl, {
   renderFile: {
       template: path.resolve(__dirname, 'swagger-ui/index.ejs'),
       data: Object.assign({
           home: '/',
           title: 'Swagger UI',
           pageTitle: 'Swagger',
           url: '/api-docs',
           auth: null
       }, options.swaggerUi)
   }
});

参数

除了methods的默认值为GET外,其余参数无区别。

自定义Engine

请参考/src/render/engine/ejs-engine.js

router.upload(methods, path, opts, ...middleware)

通过定制的busboy模块,满足文件上传的需求。并实现了FileStorageMemoryStorage来接收处理文件,还支持自定义Storage

router.upload('./upload', {
    uploadFile: {
        root: path.resolve(__dirname, 'uploads'),
        allowedFileExts: 'js,png'
    }
}, async (ctx, next) => {
    console.log(ctx.req.body);      // 除type=file外的其它fields
    console.log(ctx.req.files);     // 由Storage返回的文件对象
    console.log(ctx.request.body);  // 等价于 ctx.req.body
    console.log(ctx.request.files); // 等价于 ctx.req.files
    ctx.body = '上传成功!';
});

参数

除了methods的默认值为POST外,其余参数无区别。

自定义Storage

请参考/src/upload/storage/file.js

router.redirect(source, target, code)

路由跳转。

参数

  • source: 访问路径
  • target: 目标位置
  • code: 状态码,默认为301

router.beforeStart(callback)

在服务启动之前的通用拦截操作,此时会执行传入的callback。

参数

  • callback: Function,待执行的回调函数,入参为已定义的所有routes对象数组。

ctx

路由在处理过程中会往koactx对象上塞入一些变量。

ctx.route

当前命中路由的实例对象。

ctx.router

当前的实例化router对象。

ctx.routeOptions

从路由定义时传入的额外配置参数(opts.routeOptions),主要是为了在拦截阶段可以根据路由定义的不同routeOptions来分别处理。

ctx.params

定义在path上的参数解析之后的对象。

ctx.req.body和ctx.request.body

解析之后得到的body参数。

ctx.req.files和ctx.request.files

解析之后得到的文件上传对象。

ctx.req.formData和ctx.request.formData

解析multipart/form-data时得到的内容,即body + files

路由配置

解析body配置

  • parseBody.encoding: String,字符编码,默认为'utf8'
  • parseBody.jsonLimit: Array,Content-Typeapplication/json时的body大小,默认不超过2MB
  • parseBody.formLimit: Array,Content-Typeapplication/x-www-form-urlencoded时的body大小,默认不超过56KB
  • parseBody.jsonFormats: Array,Content-Type的值,默认为['application/json']
  • parseBody.formFormats: Array,Content-Type的值,默认为['application/x-www-form-urlencoded']
  • parseBody.onerror: Function,解析错误处理回调函数,传入参数为一个错误对象,this将指向Koacontext。如果不指定,则将返回400状态。

跨域配置

  • allowCross.origin: 设置Access-Control-Allow-Origin头,值可以是以下类型: Boolean - 为true时设置为请求头的Origin字段,为false时将禁用跨域。 String - 设置为指定值。例如指定为http://example.com时,只有从http://example.com发起的请求才能跨域。 RegExp - 当请求头的Origin字段匹配时才能跨域。例如指定为/example\.com$/时,所有以example.com结尾的网站发过来的跨域请求都将被允许。 Array - 允许列表中的多个网站跨域。 Function - 自定义是否允许跨域。第一个参数为请求头的Origin字段,第二个参数为一个回调处理函数(回调的第一个参数为err [object],第二个参数为allow [bool])。
  • allowCross.methods: 设置Access-Control-Allow-Methods头,值可以是个数组(如: ['GET', 'PUT', 'POST'])或者以英文逗号分隔的字符串(如: 'GET,PUT,POST')。
  • allowCross.allowedHeaders: 设置Access-Control-Allow-Headers头,值可以是个数组(如: ['Content-Type', 'Authorization'])或者以英文逗号分隔的字符串(如: 'Content-Type,Authorization')。如果未指定该设置,那么将自动使用请求头的Access-Control-Request-Headers字段。
  • allowCross.exposedHeaders: 设置Access-Control-Expose-Headers头,值可以是个数组(如: ['Content-Range', 'X-Content-Range'])或者以英文逗号分隔的字符串(如: 'Content-Range,X-Content-Range')。
  • allowCross.credentials: 设置Access-Control-Allow-Credentials头,值为true或false(默认为false)。当需要允许跨域传cookie时,需要设置为true。
  • allowCross.maxAge: String,设置Access-Control-Max-Age头。
  • allowCross.optionsSuccessStatus: String,设置OPTIONS请求时返回的状态码,默认为204(No Content)。

静态文件资源配置

  • staticFile.maxAge: 设置浏览器最大缓存时间(毫秒),默认为0。
  • staticFile.immutable: 设置资源是否不会变更,可以永久缓存,默认为false。
  • staticFile.hidden: 设置是否允许输出隐藏文件,默认为false。
  • staticFile.root: 设置资源文件所在的根目录,必需指定。
  • staticFile.index: 设置目录下的默认页,如果设置该值,则当用户直接访问该目录时,会自动跳转到默认页。
  • staticFile.gzip: 设置是否自动使用gzip压缩文件(.gz),默认为true。
  • staticFile.brotli: 设置是否自动使用brotli压缩文件(.br),默认为true。
  • staticFile.setHeaders: 设置自定义响应头Cache-ControlLast-Modified,该方法接受res, path, stats三个参数。
  • staticFile.extensions: Array,设置自动补全的文件扩展名,默认为false。
  • staticFile.filename: 设置静态文件对应的文件名,当为目录时不需要指定该配置。
  • staticFile.filter: Function,接收请求的filename,返回true时才能访问该静态资源文件,默认无限制。
  • staticFile.include: RegExp,限制允许访问的静态资源文件,当设置了filter时该选项将被忽略。
  • staticFile.exclude: RegExp,限制不允许访问的静态资源文件,当设置了filter时该选项将被忽略。
  • staticFile.converter: Function,接收请求的filename,返回对应的本地文件的filename。
  • staticFile.mapList: key/value,设置请求的filename到本地文件的映射关系,当设置了converter时该选项将被忽略。
  • staticFile.onerror: Function,错误处理回调函数,传入参数为一个错误对象,this将指向Koacontext。如果不指定,则将直接抛出该错误。

文件上传配置

  • uploadFile.storage: Class,上传文件处理类。当指定了uploadFile.root时,将使用FileStorage保存为本地文件,否则使用MemoryStorage保存到内存
  • uploadFile.allowedFileExts: String|Array,允许的上传文件名后缀,默认为*
  • uploadFile.allowedFileNames: String|Array,允许的上传文件名(即input的name属性),默认为*
  • uploadFile.root: String,文件保存的根目录(绝对路径)
  • uploadFile.mode: Number,文件保存目录的权限,默认为0777
  • uploadFile.multiple: Boolean,是否允许多文件上传(即input的multiple属性),默认为false
  • uploadFile.fieldNameSize: Number,允许的字段名总大小,默认为100B
  • uploadFile.fieldValueSize: Number,允许的字段值总大小,默认为1M
  • uploadFile.fieldsCount: Number,允许的字段总个数,默认不限制
  • uploadFile.fileSize: Number,允许的字段总个数,默认不限制
  • uploadFile.fieldsCount: Number,允许的字段总个数,默认不限制
  • uploadFile.fieldsCount: Number,允许的字段总个数,默认不限制
  • uploadFile.fileSize: Number,允许的单个文件大小(单位为Byte),默认10240(即10MB)
  • uploadFile.filesCount Number,允许上传的文件个数,默认不限制
  • uploadFile.partsCount Number,允许的字段+文件总个数,默认不限制
  • uploadFile.onerror Function,上传出错时的处理方式,如果不指定则将直接返回500状态

模板渲染配置

  • renderFile.engine: Class,模板渲染类。默认为ejs
  • renderFile.data Object,传入用于模板渲染的额外数据
  • renderFile.template String,模板文件的绝对路径
  • renderFile.onerror Function,渲染出错时的处理方式,如果不指定则将直接返回500状态

Swagger文档配置

  • swagger.docApi: String,api文档接口地址,默认为"/api-docs"
  • swagger.docUrl: String,swagger页面地址,默认为"/swagger-ui.html"
  • swagger.swaggerUi: Object,swagger-ui的相关配置
  • swagger.swaggerUi.home: String,首页地址,默认为"/"
  • swagger.swaggerUi.title: String,title标签内容
  • swagger.swaggerUi.pageTitle: String,页面上显示的标题内容
  • swagger.swaggerUi.url: String,api文档接口地址,默认为"/api-docs"
  • swagger.swaggerUi.auth: Object,auth配置

参数校验配置

  • validator.onerror Function,校验不匹配时的处理方式,如果不指定则将直接返回400状态

示例

更多使用示例请查看examples目录。