Request validation and parameter verification
Request Validation and Parameter Checking
Koa2, as a lightweight Node.js framework, often requires input data validation when handling HTTP requests. Proper parameter validation can effectively prevent illegal data from entering the system, improving the security and stability of the application.
Why Parameter Validation is Needed
Client-submitted data cannot be trusted and may contain malicious content or formatting errors. For example, during user registration, checks are needed to ensure the email format is correct, password strength is sufficient, age is a number, etc. Lack of validation can lead to:
- Garbage data being stored in the database
- Unexpected system errors
- Exploitation of security vulnerabilities
// Dangerous example without validation
router.post('/user', async (ctx) => {
const { username, age } = ctx.request.body
// Directly using unvalidated data
await User.create({ username, age })
})
Common Validation Methods
Manual Validation
The most basic approach is to validate directly in the route handler:
router.post('/login', async (ctx) => {
const { email, password } = ctx.request.body
if (!email) {
ctx.throw(400, 'Email cannot be empty')
}
if (!/^[\w-]+@[\w-]+\.[a-z]{2,4}$/i.test(email)) {
ctx.throw(400, 'Invalid email format')
}
if (password.length < 6) {
ctx.throw(400, 'Password must be at least 6 characters')
}
// Logic after validation passes
})
Using koa-validate
The koa-validate middleware provides convenient validation methods:
const validate = require('koa-validate')
app.use(validate())
router.post('/articles', async (ctx) => {
ctx.checkBody('title').notEmpty('Title cannot be empty').len(2, 100, 'Title length must be 2-100 characters')
ctx.checkBody('content').notEmpty('Content cannot be empty')
if (ctx.errors) {
ctx.status = 422
ctx.body = { errors: ctx.errors }
return
}
// Article creation logic
})
Best Practices for Parameter Validation
Layered Validation
It's recommended to separate validation logic from route handling:
// validators/userValidator.js
const validateUser = (data) => {
const errors = {}
if (!data.username) {
errors.username = 'Username cannot be empty'
} else if (data.username.length < 3) {
errors.username = 'Username must be at least 3 characters'
}
// Other field validations...
return {
errors,
isValid: Object.keys(errors).length === 0
}
}
// Usage in routes
router.post('/users', async (ctx) => {
const { errors, isValid } = validateUser(ctx.request.body)
if (!isValid) {
ctx.status = 422
ctx.body = { errors }
return
}
// Processing logic
})
Using Joi for Complex Validation
Joi provides powerful schema validation capabilities:
const Joi = require('joi')
const userSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{6,30}$')),
email: Joi.string().email({ minDomainSegments: 2 }),
age: Joi.number().integer().min(18).max(120)
})
router.post('/register', async (ctx) => {
const { error, value } = userSchema.validate(ctx.request.body)
if (error) {
ctx.status = 400
ctx.body = { error: error.details[0].message }
return
}
// Use the validated value
})
File Upload Validation
In addition to regular parameters, file uploads also require validation:
const upload = require('koa-multer')({
dest: 'uploads/',
fileFilter: (req, file, cb) => {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('Only image files are allowed'), false)
}
cb(null, true)
},
limits: {
fileSize: 2 * 1024 * 1024 // 2MB limit
}
})
router.post('/avatar', upload.single('avatar'), async (ctx) => {
// File has passed initial validation
const file = ctx.req.file
// Further validation
if (!file) {
ctx.throw(400, 'Please upload an avatar file')
}
// File processing
})
Custom Validation Middleware
Encapsulate common validation logic into middleware:
// middlewares/validateParams.js
const validateParams = (schema) => {
return async (ctx, next) => {
const { error } = schema.validate(ctx.request.body)
if (error) {
ctx.status = 400
ctx.body = { error: error.details[0].message }
return
}
await next()
}
}
// Usage example
const articleSchema = Joi.object({
title: Joi.string().required(),
content: Joi.string().required()
})
router.post('/articles', validateParams(articleSchema), async (ctx) => {
// Parameters have been validated
})
Unified Validation Error Handling
Create a global error-handling middleware to standardize validation error formatting:
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
if (err.isJoi) {
ctx.status = 422
ctx.body = {
message: 'Validation error',
details: err.details.map(d => ({
field: d.context.key,
message: d.message
}))
}
} else {
// Other error handling
}
}
})
Performance Optimization Recommendations
- Fail early: Perform basic validation at the front of the middleware chain
- Avoid duplicate validation: Do not recheck already validated parameters
- Cache validation rules: Especially complex Joi schemas
- Asynchronous validation: For example, checking if a username already exists
// Asynchronous validation example
const checkUsernameUnique = async (ctx) => {
const user = await User.findOne({ username: ctx.request.body.username })
if (user) {
ctx.throw(409, 'Username already exists')
}
}
router.post('/users',
validateParams(userSchema),
checkUsernameUnique,
async (ctx) => {
// Registration logic
}
)
Security Considerations
- Do not return detailed validation errors directly to the client
- Handle sensitive fields with special care (e.g., passwords, tokens)
- Protect against mass assignment attacks (e.g., using _.pick to select allowed fields)
- Validate deep objects to prevent prototype pollution
// Security example
const _ = require('lodash')
router.put('/users/:id', async (ctx) => {
// Only allow updates to specific fields
const allowedFields = ['name', 'avatar', 'bio']
const updateData = _.pick(ctx.request.body, allowedFields)
// Validate update data
const { error } = updateUserSchema.validate(updateData)
if (error) { /* Handle error */ }
await User.findByIdAndUpdate(ctx.params.id, updateData)
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn