Easy, rich and fully validated koa routing.
NodeJS >= 12
is required.
const koa = require('koa');
const router = require('koa-joi-router');
const Joi = router.Joi;
const public = router();
public.get('/', async (ctx) => {
ctx.body = 'hello joi-router!';
});
public.route({
method: 'post',
path: '/signup',
validate: {
body: {
name: Joi.string().max(100),
email: Joi.string().lowercase().email(),
password: Joi.string().max(100),
_csrf: Joi.string().token()
},
type: 'form',
output: {
200: {
body: {
userId: Joi.string(),
name: Joi.string()
}
}
}
},
handler: async (ctx) => {
const user = await createUser(ctx.request.body);
ctx.status = 201;
ctx.body = user;
}
});
const app = new koa();
app.use(public.middleware());
app.listen(3000);
koa-joi-router
returns a constructor which you use to define your routes.The design is such that you construct multiple router instances, one foreach section of your application which you then add as koa middleware.
const Koa = require("koa")
const router = require('koa-joi-router');
const pub = router();
const admin = router();
const auth = router();
// add some routes ..
pub.get('/some/path', async () => {});
admin.get('/admin', async () => {});
auth.post('/auth', async () => {});
const app = new Koa();
app.use(pub.middleware());
app.use(admin.middleware());
app.use(auth.middleware());
app.listen();
It is HIGHLY RECOMMENDED you use this bundled version of Joito avoid bugs related to passing an object created with a differentrelease of Joi into the router.
const koa = require('koa');
const router = require('koa-joi-router');
const Joi = router.Joi;
Adds a new route to the router. route()
accepts an object or array of objectsdescribing route behavior.
const router = require('koa-joi-router');
const public = router();
public.route({
method: 'post',
path: '/signup',
validate: {
header: joiObject,
query: joiObject,
params: joiObject,
body: joiObject,
maxBody: '64kb',
output: { '400-600': { body: joiObject } },
type: 'form',
failure: 400,
continueOnError: false
},
pre: async (ctx, next) => {
await checkAuth(ctx);
return next();
},
handler: async (ctx) => {
await createUser(ctx.request.body);
ctx.status = 201;
},
meta: { 'this': { is: 'stored internally with the route definition' }}
});
or
const router = require('koa-joi-router');
const public = router();
const routes = [
{
method: 'post',
path: '/users',
handler: async (ctx) => {}
},
{
method: 'get',
path: '/users',
handler: async (ctx) => {}
}
];
public.route(routes);
method
: required HTTP method like "get", "post", "put", etcpath
: required stringvalidate
header
: object which conforms to Joi validationquery
: object which conforms to Joi validationparams
: object which conforms to Joi validationbody
: object which conforms to Joi validationmaxBody
: max incoming body size for forms or json inputfailure
: HTTP response code to use when input validation fails. default 400
type
: if validating the request body, this is required. either form
, json
or multipart
formOptions
: options for co-body form parsing when type: 'form'
jsonOptions
: options for co-body json parsing when type: 'json'
multipartOptions
: options for busboy parsing when type: 'multipart'
{ limits: { files: 1 }}
autoFields
: Determines whether form fields should be auto-parsed (default: true
). See the await-busboy docs.output
: see output validationcontinueOnError
: if validation fails, this flags determines if koa-joi-router
should continue processing the middleware stack or stop and respond with an error immediately. useful when you want your route to handle the error response. default false
validateOptions
: options for Joi validate. default {}
handler
: required async function or functionspre
: async function or function, will be called before parser and validatorsmeta
: meta data about this route. koa-joi-router
ignores this but stores it along with all other route datakoa-joi-router
supports the traditional router.get()
, router.post()
type APIsas well.
const router = require('koa-joi-router');
const admin = router();
// signature: router.method(path [, config], handler [, handler])
admin.put('/thing', handler);
admin.get('/thing', middleware, handler);
admin.post('/thing', config, handler);
admin.delete('/thing', config, middleware, handler);
Middleware run in the order they are defined by .use()(or .get(), etc.) They are invoked sequentially, requests start at the first middleware and work their way "down" the middleware stack which matches Express 4 API.
const router = require('koa-joi-router');
const users = router();
users.get('/:id', handler);
users.use('/:id', runThisAfterHandler);
Defines a route prefix for all defined routes. This is handy in "mounting" scenarios.
const router = require('koa-joi-router');
const users = router();
users.get('/:id', handler);
// GET /users/3 -> 404
// GET /3 -> 200
users.prefix('/users');
// GET /users/3 -> 200
// GET /3 -> 404
Defines middleware for named route parameters. Useful for auto-loading or validation.
See @koa/router
const router = require('koa-joi-router');
const users = router();
const findUser = (id) => {
// stub
return Promise.resolve('Cheddar');
};
users.param('user', async (id, ctx, next) => {
const user = await findUser(id);
if (!user) return ctx.status = 404;
ctx.user = user;
await next();
});
users.get('/users/:user', (ctx) => {
ctx.body = `Hello ${ctx.user}`;
});
// GET /users/3 -> 'Hello Cheddar'
Generates routing middleware to be used with koa
. If this middleware isnever added to your koa
application, your routes will not work.
const router = require('koa-joi-router');
const public = router();
public.get('/home', homepage);
const app = koa();
app.use(public.middleware()); // wired up
app.listen();
The route definition for the currently matched route is availablevia ctx.state.route
. This object is not the exact same routedefinition object which was passed into koa-joi-router, nor is itused internally - any changes made to this object willnot have an affect on your running application but is availableto meet your introspection needs.
const router = require('koa-joi-router');
const public = router();
public.get('/hello', async (ctx) => {
console.log(ctx.state.route);
});
When using the validate.type
option, koa-joi-router
adds a few new propertiesto ctx.request
to faciliate input validation.
The ctx.request.body
property will be set when either of the followingvalidate.type
s are set:
When validate.type
is set to json
, the incoming data must be JSON. If it is not,validation will fail and the response status will be set to 400 or the value ofvalidate.failure
if specified. If successful, ctx.request.body
will be set to theparsed request input.
admin.route({
method: 'post',
path: '/blog',
validate: { type: 'json' },
handler: async (ctx) => {
console.log(ctx.request.body); // the incoming json as an object
}
});
When validate.type
is set to form
, the incoming data must be form data(x-www-form-urlencoded). If it is not, validation will fail and the responsestatus will be set to 400 or the value of validate.failure
if specified.If successful, ctx.request.body
will be set to the parsed request input.
admin.route({
method: 'post',
path: '/blog',
validate: { type: 'form' },
handler: async (ctx) => {
console.log(ctx.request.body) // the incoming form as an object
}
});
The ctx.request.parts
property will be set when either of the followingvalidate.type
s are set:
When validate.type
is set to multipart
, the incoming data must be multipart data.If it is not, validation will fail and the responsestatus will be set to 400 or the value of validate.failure
if specified.If successful, ctx.request.parts
will be set to anawait-busboy object.
admin.route({
method: 'post',
path: '/blog',
validate: { type: 'multipart' },
handler: async (ctx) => {
const parts = ctx.request.parts;
let part;
try {
while ((part = await parts)) {
// do something with the incoming part stream
part.pipe(someOtherStream);
}
} catch (err) {
// handle the error
}
console.log(parts.field.name); // form data
}
});
Note: if you do not specify a value for validate.type
, theincoming payload will not be parsed or validated. It is up to you toparse the incoming data however you see fit.
admin.route({
method: 'post',
path: '/blog',
validate: { },
handler: async (ctx) => {
console.log(ctx.request.body, ctx.request.parts); // undefined undefined
}
})
Validating the output body and/or headers your service generates on aper-status-code basis is supported. This comes in handy when contractsbetween your API and client are strict e.g. any change in responseschema could break your downstream clients. In a very active codebase, thisfeature buys you stability. If the output is invalid, an HTTP status 500will be used.
Let's look at some examples:
router.route({
method: 'post',
path: '/user',
validate: {
output: {
200: { // individual status code
body: {
userId: Joi.string(),
name: Joi.string()
}
}
}
},
handler: handler
});
router.route({
method: 'post',
path: '/user',
validate: {
output: {
'200,201': { // multiple individual status codes
body: {
userId: Joi.string(),
name: Joi.string()
}
}
}
},
handler: handler
});
router.route({
method: 'post',
path: '/user',
validate: {
output: {
'200-299': { // status code range
body: {
userId: Joi.string(),
name: Joi.string()
}
}
}
},
handler: handler
});
You are free to mix and match ranges and individual status codes.
router.route({
method: 'post',
path: '/user',
validate: {
output: {
'200,201,300-600': { // mix it up
body: {
userId: Joi.string(),
name: Joi.string()
}
}
}
},
handler: handler
});
Validating your output headers is also supported via the headers
property:
router.route({
method: 'post',
path: '/user',
validate: {
output: {
'200,201': {
body: {
userId: Joi.string(),
name: Joi.string()
},
headers: Joi.object({ // validate headers too
authorization: Joi.string().required()
}).options({
allowUnknown: true
})
},
'500-600': {
body: { // this rule only runs when a status 500 - 600 is used
error_code: Joi.number(),
error_msg: Joi.string()
}
}
}
},
handler: handler
});
Each router exposes it's route definitions through it's routes
property.This is helpful when you'd like to introspect the previous definitions andtake action e.g. to generate API documentation etc.
const router = require('koa-joi-router');
const admin = router();
admin.post('/thing', { validate: { type: 'multipart' }}, handler);
console.log(admin.routes);
// [ { path: '/thing',
// method: [ 'post' ],
// handler: [ [Function] ],
// validate: { type: 'multipart' } } ]
Sometimes you need RegExp
-like syntax support for your route definitions.Because path-to-regexpsupports it, so do we!
const router = require('koa-joi-router');
const admin = router();
admin.get('/blog/:year(\\d{4})-:day(\\d{2})-:article(\\d{3})', async (ctx, next) => {
console.log(ctx.request.params) // { year: '2017', day: '01', article: '011' }
});
Defining a route for multiple HTTP methods in a single shot is supported.
const router = require('koa-joi-router');
const admin = router();
admin.route({
path: '/',
method: ['POST', 'PUT'],
handler: fn
});
Often times you may need to add additional, route specific middleware to asingle route.
const router = require('koa-joi-router');
const admin = router();
admin.route({
path: '/',
method: ['POST', 'PUT'],
handler: [ yourMiddleware, yourHandler ]
});
You may want to bundle and nest middleware in different ways for reuse andorganization purposes.
const router = require('koa-joi-router');
const admin = router();
const commonMiddleware = [ yourMiddleware, someOtherMiddleware ];
admin.route({
path: '/',
method: ['POST', 'PUT'],
handler: [ commonMiddleware, yourHandler ]
});
This also works with the .get(),post(),put(),delete(), etc HTTP method helpers.
const router = require('koa-joi-router');
const admin = router();
const commonMiddleware = [ yourMiddleware, someOtherMiddleware ];
admin.get('/', commonMiddleware, yourHandler);
By default, koa-joi-router
stops processing the middleware stack when eitherinput validation fails. This means your route will not be reached. Ifthis isn't what you want, for example, if you're writing a web app which needsto respond with custom html describing the errors, set the validate.continueOnError
flag to true. You can find out if validation failed by checking ctx.invalid
.
admin.route({
method: 'post',
path: '/add',
validate: {
type: 'form',
body: {
id: Joi.string().length(10)
},
continueOnError: true
},
handler: async (ctx) => {
if (ctx.invalid) {
console.log(ctx.invalid.header);
console.log(ctx.invalid.query);
console.log(ctx.invalid.params);
console.log(ctx.invalid.body);
console.log(ctx.invalid.type);
}
ctx.body = await render('add', { errors: ctx.invalid });
}
});
npm test
runs tests + code coverage + lintnpm run lint
runs lint onlynpm run lint-fix
runs lint and attempts to fix syntax issuesnpm run test-cov
runs tests + test coveragenpm run open-cov
opens test coverage results in your browsernpm run test-only
runs tests only前言 对每个接口的传入参数进行校验,是一个Web后端项目的必备功能,有一个npm包叫Joi可以很优雅的完成这个工作,比如这样子: const schema = { userId: Joi.string() }; const {error, value} = Joi.validate({ userId: 'a string' }, schema); 复制代码 我们使用Typescript是希
表单验证的原则:前端验证为辅,后端验证为主,后端永远不要相信前端提交过来的任何内容 在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。 单纯的使用 if…else… 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性,让后端程序
在nodejs做后端开发的过程中,尤其是基于Restful的API服务,在前端发起请求的时候,如果没有对参数的校验,很容易在接口联调测试阶段出现各种上传参数名称错误/格式错误/字段遗漏等情况,这时候前后端工程师不得不一起去排查问题,有时候一个单词的拼写错误会造成大量的时间浪费,笔者在阅读《hapi.js实战》的时候了解到Joi这个包,感觉非常好用,于是团队引入了express的研发框架中,真的是好
Joi 是用于 JavaScript 的强大的模式描述语言和数据验证器,让你可以使用简单、直观且可读的语言来描述数据。 示例: const Joi = require('joi');const schema = Joi.object({ username: Joi.string() .alphanum() .min(3) .max(30)
Koa-Joi-Router Docs Generator This project is based on paul42/joi-router-swagger-docs. A node module for generating Swagger 2.0 JSONdefinitions from existing koa-joi-routerroutes. Aiming to be a repla
koa-joi-swagger Using joi schema to validate request & response, and generate swagger document to create beautiful API documents. Feature Router agnostic. Using your favorite library for validation, a
我想允许Joi在表单的标题字段中允许空格/空白。 明天和裘德一起工作。 也应该被允许 晨间漫步 此时,只有最后一个验证为真。以下是我的joi验证: 我添加了正则表达式,但没有结果。 什么是正确的方式?
在我的React-Native项目中,我用node下载了react-native-camera和react-native-qrcode-scanner库来读取qr代码,但在运行应用程序时开始出现这样的错误。 节点:internal/modules/cjs/loader:926 throw err;^ 错误:找不到模块“@hapi/joi”需要堆栈: d:\react\lcapp\lcapp\nod
本文向大家介绍React-Router怎么获取URL的参数?相关面试题,主要包含被问及React-Router怎么获取URL的参数?时的应答技巧和注意事项,需要的朋友参考一下 在之前的版本中,可以通过 this.props.location.query.bar 进行获取 在 v4 中,通过 this.props.location.search 获取,不过需要进行处理