在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:danneu/koa-bouncer开源软件地址:https://github.com/danneu/koa-bouncer开源编程语言:JavaScript 100.0%开源软件介绍:koa-bouncerAn http parameter validation library for Koa web apps.
Inspired by RocksonZeta. Works best with koa-router for routing. If you'd like to see how koa-bouncer looks in a real (demo) Koa application,
check out my koa-skeleton repository.
ExampleUsing koa-router for routing const Koa = require('koa');
const Router = require('koa-router')
const bouncer = require('koa-bouncer');
const app = new Koa();
const router = new Router();
// extends the Koa context with some methods
app.use(bouncer.middleware());
// POST /users - create user endpoint
router.post('/users', async (ctx) => {
// validate input
ctx.validateBody('uname')
.required('Username required')
.isString()
.trim()
ctx.validateBody('email')
.optional()
.isString()
.trim()
.isEmail('Invalid email format')
ctx.validateBody('password1')
.required('Password required')
.isString()
.isLength(6, 100, 'Password must be 6-100 chars')
ctx.validateBody('password2')
.required('Password confirmation required')
.isString()
.eq(ctx.vals.password1, 'Passwords must match')
// running database query last to give the other validations a chance to fail
ctx.validateBody('uname')
.check(await db.findUserByUname(ctx.vals.uname), 'Username taken')
// if we get this far, then validation succeeded.
// the validation populates a `ctx.vals` object with validated values
//=> { uname: 'foo', password1: 'secret', password2: 'secret' }
console.log(ctx.vals)
const user = await db.insertUser({
uname: ctx.vals.uname,
email: ctx.vals.email,
password: ctx.vals.password1
})
ctx.redirect(`/users/${user.id}`)
})
app
.use(router.routes())
.use(router.allowedMethods());
The general ideaThe idea is that koa-bouncer exposes methods that transform and assert against user-input (form submissions, request bodies, query strings) within your routes. If an assertion fails, then koa-bouncer throws a If validation succeeds, then you can access the validated/transformed
parameters in a UsageFirst, you need to inject bouncer's middleware: bouncer.middleware(opts) This extends the Koa context with these methods for you to use in routes, the bulk of the koa-bouncer abstraction:
The first three methods return a validator that targets the value in the url param, query param, or body param that you specified with 'key'. When you spawn a validator, it immediately populates Just by calling these methods, they will begin populating router.get('/search', async (ctx) => {
ctx.validateQuery('keyword')
ctx.validateQuery('sort')
ctx.body = JSON.stringify(ctx.vals)
}) curl http://localhost:3000/search
=> {}
curl http://localhost:3000/search?sort=age
=> { "sort": "age" } We can use And we can use router.get('/search', async (ctx) => {
ctx.validateQuery('keyword').required().isString().trim()
ctx.validateQuery('sort').toArray()
ctx.body = JSON.stringify(ctx.vals)
}) curl http://localhost:3000/search
=> Uncaught ValidationError
curl http://localhost:3000/search?keyword=hello
=> { "keyword": "hello", "sort": [] }
curl http://localhost:3000/search?keyword=hello&sort=age
=> { "keyword": "hello", "sort": ["age"] }
curl http://localhost:3000/search?keyword=hello&sort=age&sort=height
=> { "keyword": "hello", "sort": ["age", "height"] } If a validation fails, then the validator throws a bouncer.ValidationError that we can catch with upstream middleware. For example, we can decide that upon validation error, we redirect the user back to whatever the previous page was and populate a temporary flash object with the error and their parameters so that we can repopulate the form. app.use(async (ctx, next) => {
try {
await next();
} catch(err) {
if (err instanceof bouncer.ValidationError) {
ctx.flash = {
message: ['danger', err.message],
params: ctx.request.body
};
return ctx.redirect('back');
}
throw err;
}
});
router.post('/users', async (ctx) => {
ctx.validateBody('username')
.required('Username is required')
.isString()
.trim()
.isLength(3, 15, 'Username must be 3-15 chars');
const user = await database.insertUser(ctx.vals.username);
ctx.body = 'You successfully registered';
}); http --form POST localhost:3000/users
=> 302 Redirect to GET /users, message='Username is required'
http --form POST localhost:3000/users username=bo
=> 302 Redirect to GET /users, message='Username must be 3-15 chars'
http --form POST localhost:3000/users username=freeman
=> 200 OK, You successfully registered You can pass options into the Here are the default ones: app.use(bouncer.middleware({
getParams({params}) { return params; },
getQuery({query}) { return query; },
getBody({request}) { return request.body; }
})); You can override these if the validators need to look in a different place
to fetch the respective keys when calling the You can always define custom validators via const Validator = require('koa-bouncer').Validator;
Validator.addMethod('isValidBitcoinAddress', function(tip = 'Invalid Bitcoin address') {
// Will thread the tip through the nested assertions
this
.isString(tip)
.trim()
// Must be alphanumeric from start to finish
.match(/^[a-z0-9]+$/i, tip)
// But must not contain any of these chars
.notMatch(/[0O1l]/, tip);
return this;
}); Maybe put that in a Now you can use the custom validator method in a route or middleware: ctx.validateBody('address')
.required()
.isValidBitcoinAddress(); These chains always return the underlying validator instance. You can access
its value at any instant with const validator = ctx.validateBody('address')
.required()
.isValidBitcoinAddress();
console.log("current value of ctx.vals['address'] is", validator.val()); Here's how you'd write a validator method that transforms the underlying value: Validator.addMethod('add10', function() {
this.tap(val => val + 10);
return this;
}); In other words, just use Validator methods.val()Returns the current value currently inside the validator. router.get('/search', async (ctx) => {
const validator1 = ctx.validateQuery('q').required();
const validator2 = ctx.validateQuery('sort').optional();
ctx.body = JSON.stringify([validator1.val(), validator2.val()]);
}); curl http://localhost:3000/search?q=hello&sort=created_at
// 200 OK ["hello", "created_at"] I rarely use this method inside a route and prefer to access
values from the .required([tip])Only fails if val is ctx.validateBody('username')
.required('Must provide username') .optional()If val is This is so that you can validate a val only if user provided one. ctx.validateBody('email')
.optional()
.isEmail('Invalid email format') // Only called if ctx.request.body is `undefined` ctx.validateBody('email')
.tap(x => '[email protected]')
.optional()
.isEmail() // Always called since we are ensuring that val is always defined Mutating ctx.validateBody('email').optional();
ctx.vals.email = '[email protected]';
ctx.validateBody('email').isEmail(); // This will run You can see the optional state of a validator with its const validator = ctx.validateBody('email').optional();
console.log(validator.isOptional()); //=> true
ctx.vals.email = '[email protected]';
console.log(validator.isOptional()); //=> false
validator.isEmail(); // This will run The reason koa-bouncer considers empty strings to be unset (instead of
just Also, note that .isIn(array, [tip])Ensure val is included in given array (=== comparison). ctx.validateBody('role')
.required('Must provide a role')
.isIn(['banned', 'member', 'mod', 'admin'], 'Invalid role') .isNotIn(array, [tip])Ensure val is not included in given array (=== comparison). ctx.validateBody('favorite-fruit')
.isNotIn(['apple', 'pomegranate'], 'You cannot choose forbidden fruit') .defaultTo(defaultVal)If val is ctx.validateBody('multiplier')
.defaultTo(1.0)
.toFiniteFloat('multiplier must be a valid number') .isString([tip])Ensure val is a string. Note: Also works with strings created via ctx.validateBody('username')
.isString() It's a good practice to always call one of the .isArray([tip])Ensure val is an Array. ctx.validateQuery('recipients')
.isArray('recipients must be an array') curl http://localhost:3000/?recipients=joey
=> ValidationError
curl http://localhost:3000/?recipients=joey&recipients=kate&recipients=max
=> 200 OK, ctx.vals => ['joey', 'kate', 'max'] Note: The previous example can be improved with ctx.validateQuery('recipients')
.toArray()
.isArray('recipients must be an array') curl http://localhost:3000/?recipients=joey
=> 200 OK, ctx.vals.recipients => ['joey']
curl http://localhost:3000/?recipients=joey&recipients=kate&recipients=max
=> 200 OK, ctx.vals.recipients => ['joey', 'kate', 'max'] .eq(otherVal::Number, [tip])Ensures ctx.validateBody('house-edge')
.eq(0.01, 'House edge must be 1%') .gt(otherVal::Number, [tip])Ensures |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论