Advanced Form Validation
In this assignment, you will enhance the validation rules for your three forms (contact, registration, and login) to ensure data quality, prevent common errors, and improve security. While your forms currently have basic validation, production applications require more comprehensive validation to handle edge cases, prevent malicious input, and provide clear feedback when data does not meet requirements.
You will add maximum length constraints to prevent database errors and potential attacks, implement pattern matching to ensure proper data formatting, create custom validation logic for specific business rules, and enhance password requirements to improve security. These validation improvements will make your application more robust and user-friendly.
Understanding Comprehensive Validation
Basic validation ensures that required fields are present and meet minimum standards, but comprehensive validation goes further. Real-world applications must handle users who accidentally enter too much text, intentionally try to break the system, or simply make honest mistakes with formatting. Your database columns have maximum lengths, and exceeding these limits causes errors. Pattern matching ensures data conforms to expected formats, making it easier to process and display correctly.
Security is another crucial consideration. Password requirements that enforce complexity make accounts harder to compromise. Input validation that rejects suspicious patterns can help prevent spam and abuse. Validation that checks for proper formatting prevents issues when data is used in different contexts throughout your application.
Express-validator provides powerful tools for implementing these checks. You have already used basic validators like isLength() and isEmail(). This assignment introduces additional validators like matches() for pattern matching, custom() for business logic, and notEmpty() for ensuring fields contain data. You will also learn to chain multiple validation rules together to create comprehensive checks that catch various types of invalid input.
Preparation
Before enhancing your form validation, you need to understand three key concepts: validation rule chaining, regular expressions for pattern matching, and custom validation functions. These concepts form the foundation for the comprehensive validation you will implement across your forms.
Validation Rule Chaining
Express-validator allows you to chain multiple validation methods together. Each validator in the chain runs in order, and each can have its own custom error message. This pattern lets you check for multiple conditions on a single field while providing specific feedback for each type of failure.
Here is an example structure showing how validation rules chain together:
body('fieldName')
.trim() // Sanitizer: removes whitespace
.isLength({ min: 2, max: 100 }) // Validator: checks length
.withMessage('Custom error for length') // Error message for previous validator
.matches(/^[a-zA-Z\s]+$/) // Validator: checks pattern
.withMessage('Custom error for pattern') // Error message for previous validator
The trim() sanitizer runs first, removing leading and trailing whitespace. Then isLength() validates that the trimmed value meets length requirements. If that check fails, the associated withMessage() provides the error text. If length passes, matches() checks the pattern. Each validator has its own error message, so users see specific feedback about what went wrong.
Sanitizers like trim() and normalizeEmail() modify the input before validation runs. They clean up user data to make validation more reliable. Validators like isLength() and matches() check conditions but do not modify data. Always run sanitizers before validators.
Regular Expressions for Pattern Matching
The matches() validator uses regular expressions to check if text follows a specific pattern. Regular expressions might look intimidating, but you can understand them by breaking them into parts.
For example, /^[a-zA-Z\s'-]+$/ validates names:
-
^means the pattern must match from the start of the string -
[a-zA-Z\s'-]is a character class that allows lowercase letters (a-z), uppercase letters (A-Z), whitespace (\s), hyphens (-), and apostrophes (') -
+means one or more of the preceding character class -
$means the pattern must match to the end of the string
Another example, /^[a-zA-Z0-9\s\-.,!?]+$/ validates subject lines:
- Allows letters and numbers (a-zA-Z0-9)
- Allows spaces (\s)
- Allows basic punctuation (\-.,!?)
- The backslash before the hyphen (\-) escapes it so it is treated as a literal character
You do not need to become a regular expression expert for this assignment. The patterns provided are tested and work correctly. Understanding their basic structure helps you modify them if needed.
Custom Validation Functions
Sometimes you need validation logic that goes beyond what built-in validators provide. The custom() validator lets you write your own validation function. This function receives the field value and must either return true for valid input or throw an error for invalid input.
Here is a custom validator that checks for spam patterns:
body('message')
.custom((value) => {
// Split message into words
const words = value.split(/\s+/);
// Create a Set of unique words
const uniqueWords = new Set(words);
// If message has many words but few unique ones, likely spam
if (words.length > 20 && uniqueWords.size / words.length < 0.3) {
throw new Error('Message appears to be spam');
}
// Valid input must return true
return true;
})
This validator calculates the ratio of unique words to total words. If a message has more than 20 words but less than 30% of them are unique (meaning lots of repetition), it throws an error. This catches messages like "buy now buy now buy now" that spam bots often send. Legitimate messages naturally have more variety in word choice.
Assignment Instructions
1. Enhance Contact Form Validation
Open src/controllers/forms/contact.js and locate your validation array. Currently it only checks minimum lengths for subject and message fields. You need to add maximum length constraints, pattern matching for the subject, and custom spam detection for the message.
Replace your current validation array with this enhanced version:
router.post('/',
[
body('subject')
.trim()
.isLength({ min: 2, max: 255 })
.withMessage('Subject must be between 2 and 255 characters')
.matches(/^[a-zA-Z0-9\s\-.,!?]+$/)
.withMessage('Subject contains invalid characters'),
body('message')
.trim()
.isLength({ min: 10, max: 2000 })
.withMessage('Message must be between 10 and 2000 characters')
.custom((value) => {
// Check for spam patterns (excessive repetition)
const words = value.split(/\s+/);
const uniqueWords = new Set(words);
if (words.length > 20 && uniqueWords.size / words.length < 0.3) {
throw new Error('Message appears to be spam');
}
return true;
})
],
handleContactSubmission
);
Save the file and test your enhanced validation. Add novalidate to your contact form view and try submitting a subject with 300 characters. You should see the error message. Try submitting a subject with special characters like @ or #. You should see the invalid characters error. Try submitting a message that repeats the same word 30 times. You should see the spam detection error.
The 255 character limit for subject matches your database column definition (VARCHAR(255)). The 2000 character limit for message is a reasonable maximum that prevents abuse while allowing detailed messages. These limits protect your database from errors that occur when data exceeds column sizes.
The spam detection custom validator uses a mathematical approach to identify repetitive messages. Before moving on, take a moment to study the code and think about why comparing the number of unique words to total words would catch spam. What ratio would a normal message have? What would a spam message look like? Formulate your reasoning, then share the code snippet and your explanation with your AI assistant to verify your understanding.
2. Enhance Registration Form Validation
Open src/controllers/forms/registration.js and locate the registrationValidation array. You need to enhance validation for all fields: name, email, emailConfirm, password, and passwordConfirm.
Update your registrationValidation array with these requirements:
Name field:
-
Add maximum length of 100 characters to the existing
isLength():isLength({ min: 2, max: 100 }) -
Update the error message to:
'Name must be between 2 and 100 characters' -
Add pattern matching:
matches(/^[a-zA-Z\s'-]+$/) -
Add error message for pattern:
'Name can only contain letters, spaces, hyphens, and apostrophes'
Email field:
-
Keep
trim(),isEmail(), andnormalizeEmail()as they are -
Add maximum length check after normalizeEmail:
isLength({ max: 255 }) -
Add error message:
'Email address is too long'
Password field:
-
Update
isLength()to include maximum:isLength({ min: 8, max: 128 }) -
Update error message to:
'Password must be between 8 and 128 characters' -
Keep the existing
matches(/[0-9]/)rule and its error message -
Add new rule for lowercase:
matches(/[a-z]/)with message'Password must contain at least one lowercase letter' -
Add new rule for uppercase:
matches(/[A-Z]/)with message'Password must contain at least one uppercase letter' -
Update the special character rule to use this pattern:
matches(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/) - Keep the existing special character error message
EmailConfirm and PasswordConfirm fields:
- Keep these as they are (they already use custom validation)
Your complete registrationValidation array should now validate name format and length, email length, and password complexity with uppercase, lowercase, number, and special character requirements. Test thoroughly with novalidate on your form.
When writing regular expressions in your code, you need to escape special characters. The & symbol in the special character pattern is written as & in this HTML document but should be just & in your JavaScript code. The backslashes and brackets are already properly escaped in the pattern provided above.
3. Enhance Login Form Validation
Open src/controllers/forms/login.js and locate the loginValidation array. Login forms need less validation than registration forms because users are entering existing data, not creating new records. However, you should still validate length constraints and ensure fields are not empty.
Update your loginValidation array with these requirements:
Email field:
- Keep all existing validation (trim, isEmail, normalizeEmail)
-
Add maximum length check:
isLength({ max: 255 }) -
Add error message:
'Email address is too long'
Password field:
-
Add
notEmpty()before the existingisLength() -
Keep the error message for notEmpty as
'Password is required' -
Update
isLength()to:isLength({ min: 8, max: 128 }) -
Add error message:
'Password must be between 8 and 128 characters'
Notice that login validation does not check password complexity (uppercase, lowercase, special characters). This is intentional. Users created their passwords during registration where those rules were enforced. During login, you only verify length constraints to catch obvious errors while allowing any valid existing password.
4. Test All Enhanced Validation
Thoroughly test each form with various invalid inputs to ensure your validation rules work correctly. Remember to add novalidate to each form temporarily to bypass browser validation and test your server-side rules.
Contact Form Testing:
- Submit with a subject over 255 characters (copy and paste a long paragraph)
- Submit with a subject containing @ or # symbols
- Submit with a message over 2000 characters
- Submit with a message that repeats the same word many times
Registration Form Testing:
- Submit with a name containing numbers or special characters
- Submit with a name over 100 characters
- Submit with an extremely long email address (over 255 characters)
- Submit with a password that has no uppercase letters
- Submit with a password that has no lowercase letters
- Submit with a password that has no numbers
- Submit with a password that has no special characters
- Submit with a password over 128 characters
Login Form Testing:
- Submit with an empty password field
- Submit with an extremely long email address
- Submit with a password over 128 characters
For each test, verify that you see appropriate flash error messages and that the validation prevents submission. After confirming all validation works, remove the novalidate attributes from your forms to restore client-side validation.
When a field fails multiple validation rules, you will see multiple flash error messages. For example, a password with no uppercase letters and no numbers will generate two error messages. This comprehensive feedback helps users understand all the requirements they need to meet.
Key Concepts Summary
This assignment enhanced your form validation with comprehensive rules that improve data quality and security. You added maximum length constraints that prevent database errors and protect against certain attacks. Pattern matching with regular expressions ensures data conforms to expected formats. Custom validation functions implement business logic that built-in validators cannot handle.
The validation improvements work together with the flash message system you implemented previously. When validation fails, users see specific, actionable error messages that explain exactly what needs to be fixed. This combination of server-side validation and user feedback creates a robust form handling system.
You practiced chaining multiple validators together, understanding that each validator in the chain can have its own error message. Sanitizers like trim() run first to clean data, then validators check conditions. Password validation demonstrates how multiple matches() calls can enforce complex requirements by checking separate patterns.
The distinction between registration and login validation is important. Registration validates that new data meets all requirements, while login validates that existing data is formatted correctly without re-checking creation rules. This approach balances security with usability.
Experiment and Extend
Consider these enhancements to further improve your validation:
- Add validation to check that email domains are realistic (reject obvious fake domains)
- Create a custom validator that checks password strength and provides feedback beyond basic requirements
- Add validation to your contact form that detects and blocks messages with too many URLs (another spam indicator)
- Implement field-specific error display in your views so errors appear next to their fields rather than at the top of the page
Conclusion
Comprehensive validation is essential for production web applications. Basic validation catches obvious errors, but real-world applications face users who make mistakes, exceed limits, or attempt malicious input. The validation enhancements you implemented protect your database, improve security, and provide better user feedback.
Express-validator provides powerful tools for implementing validation rules that are both comprehensive and maintainable. Chaining validators together, using regular expressions for pattern matching, and writing custom validation functions give you complete control over what data your application accepts. These skills apply to any form you create in future projects.