HTTP Request Handling
Request System
Comprehensive HTTP request handling with body parsing, validation, authentication, and session management.
Request Object Overview
Data Access
Body parsing, parameters, query strings, headers, and cookies
Validation
Built-in validation rules with automatic error handling
Authentication
User authentication, sessions, and authorization helpers
Basic Request Properties
Access HTTP methods, paths, URIs, and headers
dart
// Basic request properties
router.get('/api/profile', (req, res) async {
// HTTP method
final method = req.method; // 'GET'
// Request path
final path = req.path; // '/api/profile'
// Full URI
final uri = req.uri; // Uri object
// Query parameters
final page = req.query['page']; // '1'
final limit = req.query['limit']; // '10'
// Headers
final userAgent = req.userAgent;
final contentType = req.contentType;
final isAjax = req.isAjax();
final acceptsJson = req.acceptsJson();
res.sendJson({
'method': method,
'path': path,
'query': req.query,
'headers': {
'user_agent': userAgent,
'content_type': contentType,
'is_ajax': isAjax,
'accepts_json': acceptsJson,
}
});
});HTTP Properties
methodGET, POST, PUT, etc.pathRequest path (/api/users)uriFull URI objectqueryQuery parameters mapHeader Properties
contentTypeContent-Type headeruserAgentUser-Agent headeracceptsJson()Accepts JSON checkisAjax()AJAX request checkBody Parsing
Parse JSON, form data, and multipart requests
dart
// JSON request body
router.post('/api/users', (req, res) async {
final body = await req.body;
// Access specific fields
final name = req.input('name');
final email = req.input('email');
final age = req.input('age', 18); // with default
// Check if field exists
if (req.has('name')) {
// Field exists
}
res.sendJson({
'received': body,
'name': name,
'email': email,
'age': age,
});
});
// Form data
router.post('/api/contact', (req, res) async {
final formData = await req.body;
final message = req.input('message');
final priority = req.input('priority', 'normal');
// Process form data...
res.sendJson({
'message': 'Form submitted',
'data': formData,
});
});Supported Content Types
application/jsonJSON data
application/x-www-form-urlencodedForm data
multipart/form-dataFile uploads
File Uploads
Handle single and multiple file uploads with validation
dart
// Single file upload
router.post('/api/avatar', (req, res) async {
if (!req.hasFile('avatar')) {
return res.badRequest().sendJson({'error': 'No avatar file provided'});
}
final avatarFile = req.file('avatar');
if (avatarFile == null) {
return res.badRequest().sendJson({'error': 'Invalid avatar file'});
}
// Validate file size (max 2MB)
if (avatarFile.size > 2 * 1024 * 1024) {
return res.badRequest().sendJson({'error': 'File too large'});
}
// Validate file type
final allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.contains(avatarFile.contentType)) {
return res.badRequest().sendJson({'error': 'Invalid file type'});
}
// Save file
final filename = 'avatar_${DateTime.now().millisecondsSinceEpoch}.${avatarFile.extension}';
final path = 'storage/avatars/$filename';
await avatarFile.saveTo(path);
res.sendJson({
'message': 'Avatar uploaded successfully',
'filename': filename,
'path': path,
});
});
// Multiple file upload
router.post('/api/gallery', (req, res) async {
final uploadedFiles = req.filesByName('images');
if (uploadedFiles.isEmpty) {
return res.badRequest().sendJson({'error': 'No images provided'});
}
final savedFiles = [];
for (final file in uploadedFiles) {
final filename = 'gallery_${DateTime.now().millisecondsSinceEpoch}_${file.filename}';
final path = 'storage/gallery/$filename';
await file.saveTo(path);
savedFiles.add({
'original_name': file.filename,
'saved_name': filename,
'size': file.size,
'type': file.contentType,
});
}
res.sendJson({
'message': '${savedFiles.length} images uploaded',
'files': savedFiles,
});
});File Properties
dart
final avatarFile = req.file('avatar');
// File properties
final filename = avatarFile.filename; // 'profile.jpg'
final size = avatarFile.size; // 1024000 (bytes)
final contentType = avatarFile.contentType; // 'image/jpeg'
final extension = avatarFile.extension; // 'jpg'
final fieldName = avatarFile.fieldName; // 'avatar'
// File content
final asString = avatarFile.asString(); // File content as string
final asBytes = avatarFile.data; // Raw bytesFile Methods
saveTo(path)Save to diskasString()Get as stringsizeFile size in bytesextensionFile extensionInput Validation
Validate request data with comprehensive rules
dart
// Request validation
router.post('/api/users', (req, res) async {
try {
// Validate request body
final validatedData = await req.validate({
'name': 'required|min:2|max:100',
'email': 'required|email|unique:users,email',
'password': 'required|min:8',
'age': 'optional|integer|min:18|max:120',
});
// Data is validated and sanitized
final user = await User.create(validatedData);
res.created().sendJson({'user': user});
} catch (e) {
// Validation failed - error response sent automatically
// No need to handle manually
}
});
// Custom validation with specific data
router.put('/api/users/:id', (req, res) async {
final userId = req.param('id');
final updateData = await req.body;
try {
final validatedData = req.validateData(updateData, {
'name': 'optional|min:2|max:100',
'email': 'optional|email',
'bio': 'optional|max:500',
});
final user = await User.find(userId);
await user.update(validatedData);
res.sendJson({'user': user});
} catch (e) {
// Validation failed
}
});
// File upload validation
router.post('/api/documents', (req, res) async {
try {
final validatedData = await req.validate({
'title': 'required|min:3|max:200',
'document': 'required|file|max:10MB|mimes:pdf,doc,docx',
'category': 'required|in:contract,report,invoice',
});
final documentFile = req.file('document');
// Process validated file...
res.sendJson({'message': 'Document uploaded successfully'});
} catch (e) {
// Validation failed
}
});Validation Features
Comprehensive Rules
Built-in validation rules for common scenarios
Automatic Errors
Error formatting and response handling
Custom Messages
Customize validation error messages
File Validation
Validate file uploads and types
Parameters & Query Strings
Access route parameters and query string data
dart
// Path parameters
router.get('/api/users/:id', (req, res) async {
final userId = req.param('id'); // Get path parameter
if (userId == null) {
return res.badRequest().sendJson({'error': 'User ID required'});
}
final user = await User.find(userId);
res.sendJson({'user': user});
});
// Multiple path parameters
router.get('/api/posts/:category/:slug', (req, res) async {
final category = req.param('category');
final slug = req.param('slug');
final post = await Post.where('category', category)
.where('slug', slug)
.first();
res.sendJson({'post': post});
});
// Query parameters
router.get('/api/search', (req, res) async {
final query = req.query['q'] ?? '';
final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
final limit = int.tryParse(req.query['limit'] ?? '10') ?? 10;
final sort = req.query['sort'] ?? 'created_at';
final order = req.query['order'] ?? 'desc';
// Build search query...
final results = await Search.query(query)
.orderBy(sort, direction: order)
.paginate(perPage: limit, page: page);
res.sendJson({
'query': query,
'results': results.data,
'pagination': {
'page': results.currentPage,
'limit': limit,
'total': results.total,
'pages': results.lastPage,
}
});
});Path Parameters
dart
// Path parameter access
final userId = req.param('id');
final category = req.param('category');
final slug = req.param('slug');
// With default values
final page = req.param('page') ?? '1';
final limit = req.param('limit') ?? '10';
// Check if parameter exists
if (req.params.hasParam('id')) {
// Parameter exists
}Query Parameters
dart
// Query parameter access
final search = req.query['q'];
final page = req.query['page'];
final limit = req.query['limit'];
final sort = req.query['sort'];
// All query parameters
final allParams = req.query; // Map<String, String>Authentication & Authorization
Handle user authentication and role-based access
dart
// Authentication checks
router.get('/api/profile', (req, res) async {
if (!req.isAuthenticated) {
return res.unauthorized().sendJson({
'error': 'Authentication required'
});
}
final user = req.user;
res.sendJson({'user': user});
});
// Role-based authorization
router.get('/api/admin/users', (req, res) async {
if (!req.isAuthenticated) {
return res.unauthorized().sendJson({'error': 'Not authenticated'});
}
if (!req.hasRole('admin')) {
return res.forbidden().sendJson({'error': 'Admin access required'});
}
const users = await User.all();
res.sendJson({'users': users});
});
// Multiple role check
router.get('/api/moderator/content', (req, res) async {
if (!req.hasAnyRole(['admin', 'moderator'])) {
return res.forbidden().sendJson({'error': 'Insufficient permissions'});
}
// User has admin or moderator role
const content = await Content.needingModeration();
res.sendJson({'content': content});
});
// Web authentication helpers
router.get('/dashboard', (req, res) async {
if (!req.isWebAuthenticated) {
return await res.redirect('/login');
}
final user = await req.getWebUser();
const viewData = req.webViewData;
// Render dashboard with user data
await res.view('dashboard', data: viewData);
});
// CSRF protection
router.post('/api/posts', (req, res) async {
if (!req.validateCsrfToken(req.input('csrf_token'))) {
return res.forbidden().sendJson({'error': 'Invalid CSRF token'});
}
// Process form...
res.sendJson({'message': 'Post created'});
});Auth Features
Authentication State
Check if user is authenticated
Role Authorization
Role-based access control
Web Auth Helpers
Web authentication utilities
CSRF Protection
Token validation for security
Session Management
Store and retrieve session data across requests
dart
// Session management
router.post('/login', (req, res) async {
final credentials = await req.body;
final user = await authenticateUser(credentials);
if (user != null) {
// Store user data in session
req.setSession('user_id', user.id);
req.setSession('user_name', user.name);
req.setSession('user_role', user.role);
// Flash success message
req.flashSession('success', 'Login successful!');
res.sendJson({'message': 'Login successful'});
} else {
res.unauthorized().sendJson({'error': 'Invalid credentials'});
}
});
// Access session data
router.get('/api/user', (req, res) async {
final userId = req.getSession('user_id');
final userName = req.getSession('user_name');
if (userId == null) {
return res.unauthorized().sendJson({'error': 'Not authenticated'});
}
res.sendJson({
'user_id': userId,
'user_name': userName,
});
});
// Session utilities
router.get('/api/session/info', (req, res) async {
res.sendJson({
'session_id': req.sessionId,
'is_empty': req.isSessionEmpty,
'length': req.sessionLength,
'keys': req.sessionKeys.toList(),
'is_valid': req.isSessionValid(),
'created_at': req.getSessionCreatedAt(),
'last_access': req.getSessionLastAccess(),
});
});
// Session cleanup
router.post('/logout', (req, res) async {
req.destroySession();
res.sendJson({'message': 'Logged out successfully'});
});Session Methods
getSession(key)Get valuesetSession(key, value)Set valueflashSession(key, value)Flash datapullSession(key)Get & removeFlash Messages
dart
// Flash messages
router.post('/contact', (req, res) async {
try {
// Process contact form
await processContactForm(await req.body);
req.flashSession('success', 'Message sent successfully!');
res.sendJson({'message': 'Message sent'});
} catch (e) {
req.flashSession('error', 'Failed to send message');
res.badRequest().sendJson({'error': 'Failed to send message'});
}
});
// Retrieve flash messages
router.get('/messages', (req, res) async {
final successMessage = req.pullSession('success');
final errorMessage = req.pullSession('error');
res.sendJson({
'messages': {
if (successMessage != null) 'success': successMessage,
if (errorMessage != null) 'error': errorMessage,
}
});
});Cookies
Manage HTTP cookies with security features
dart
// Cookie management
router.post('/login', (req, res) async {
final credentials = await req.body;
final user = await authenticateUser(credentials);
if (user != null) {
// Set authentication cookie
req.cookieHandler.set(
'auth_token',
generateAuthToken(user),
httpOnly: true,
secure: true,
maxAge: Duration(days: 7)
);
// Set remember token if requested
if (req.input('remember') == true) {
req.cookieHandler.set(
'remember_token',
generateRememberToken(user),
maxAge: Duration(days: 30)
);
}
res.sendJson({'message': 'Login successful'});
}
});
// Access cookies
router.get('/api/user', (req, res) async {
final authToken = req.cookie('auth_token');
final rememberToken = req.rememberToken;
if (authToken == null && rememberToken == null) {
return res.unauthorized().sendJson({'error': 'Not authenticated'});
}
// Validate tokens...
res.sendJson({'message': 'Authenticated'});
});
// Cookie utilities
router.get('/cookies', (req, res) async {
res.sendJson({
'all_cookies': req.cookies,
'auth_token': req.cookie('auth_token'),
'has_remember_token': req.hasRememberToken(),
'has_csrf_token': req.hasCsrfToken(),
'csrf_token': req.csrfToken,
});
});
// Clear cookies
router.post('/logout', (req, res) async {
req.cookieHandler.delete('auth_token');
req.cookieHandler.delete('remember_token');
res.sendJson({'message': 'Logged out'});
});Cookie Features
Secure Handling
Secure cookie management
HttpOnly Support
HttpOnly flag protection
Auto Expiration
Automatic expiration handling
Remember Tokens
Persistent authentication tokens
Advanced Patterns
Custom attributes and request context
Custom Attributes
dart
// Custom attributes
server.addMiddlewareHandler((req, res, next) async {
// Add custom data to request
req.setAttribute('request_id', generateRequestId());
req.setAttribute('start_time', DateTime.now());
req.setAttribute('user_agent_parsed', parseUserAgent(req.userAgent));
await next();
});
router.get('/api/data', (req, res) async {
// Access custom attributes
final requestId = req.attribute<String>('request_id');
final startTime = req.attribute<DateTime>('start_time');
final userAgentInfo = req.attribute<Map<String, dynamic>>('user_agent_parsed');
// Use attributes in response
res.sendJson({
'request_id': requestId,
'processing_time': DateTime.now().difference(startTime),
'user_agent': userAgentInfo,
});
});Store custom data during request lifecycle
Request Context
dart
// Request context across middleware
class AuthMiddleware implements Middleware {
@override
Future<void> handle(Request req, Response res, NextFunction next) async {
final token = req.header('authorization');
if (token != null) {
try {
final user = await validateToken(token);
req.setAttribute('user', user);
req.setUser(user); // Also set in auth system
} catch (e) {
// Invalid token
}
}
await next();
}
}
class LoggingMiddleware implements Middleware {
@override
Future<void> handle(Request req, Response res, NextFunction next) async {
final startTime = DateTime.now();
req.setAttribute('start_time', startTime);
await next();
final duration = DateTime.now().difference(startTime);
final user = req.attribute<Map<String, dynamic>>('user');
print('Request: ${req.method} ${req.path}');
print('User: ${user?['id'] ?? 'anonymous'}');
print('Duration: ${duration.inMilliseconds}ms');
}
}Access request data across middleware and handlers
Complete API Example
Full CRUD implementation with all request features
dart
// Complete request handling example
import 'package:khadem/khadem_dart.dart';
class UserController {
static Future index(Request req, Response res) async {
// Check authentication
if (!req.isAuthenticated) {
return res.unauthorized().sendJson({'error': 'Not authenticated'});
}
// Parse query parameters
final page = int.tryParse(req.query['page'] ?? '1') ?? 1;
final limit = int.tryParse(req.query['limit'] ?? '10') ?? 10;
final search = req.query['search'];
// Build query
var query = User.query();
if (search != null && search.isNotEmpty) {
query = query.where('name', 'LIKE', '%$search%')
.orWhere('email', 'LIKE', '%$search%');
}
// Get paginated results
final users = await query.paginate(perPage: limit, page: page);
res.sendJson({
'data': users.data.map((user) => user.toJson()).toList(),
'pagination': {
'page': users.currentPage,
'per_page': limit,
'total': users.total,
'last_page': users.lastPage,
'has_next': users.currentPage < users.lastPage,
'has_prev': users.currentPage > 1,
}
});
}
static Future store(Request req, Response res) async {
try {
// Validate input
final validatedData = await req.validate({
'name': 'required|min:2|max:100',
'email': 'required|email|unique:users,email',
'password': 'required|min:8|confirmed',
'avatar': 'optional|file|max:2MB|mimes:jpeg,png,gif',
});
// Handle file upload if provided
if (req.hasFile('avatar')) {
final avatarFile = req.file('avatar')!;
final filename = 'avatar_${DateTime.now().millisecondsSinceEpoch}.${avatarFile.extension}';
await avatarFile.saveTo('storage/avatars/$filename');
validatedData['avatar'] = filename;
}
// Hash password
validatedData['password'] = hashPassword(validatedData['password']);
// Create user
final user = await User.create(validatedData);
// Set session
req.setSession('user_id', user.id);
req.flashSession('success', 'Account created successfully!');
res.created().sendJson({
'user': user.toJson(),
'message': 'Account created successfully'
});
} catch (e) {
// Flash input for form repopulation
req.flashInput(await req.body);
req.flashSession('error', 'Failed to create account');
res.badRequest().sendJson({
'error': 'Validation failed',
'message': 'Please check your input and try again'
});
}
}
static Future show(Request req, Response res) async {
final userId = req.param('id');
if (userId == null) {
return res.badRequest().sendJson({'error': 'User ID required'});
}
final user = await User.find(userId);
if (user == null) {
return res.notFound().sendJson({'error': 'User not found'});
}
// Check if user can view this profile
if (!req.isAuthenticated || (req.userId != user.id && !req.hasRole('admin'))) {
return res.forbidden().sendJson({'error': 'Access denied'});
}
res.sendJson({'user': user.toJson()});
}
static Future update(Request req, Response res) async {
final userId = req.param('id');
if (userId == null) {
return res.badRequest().sendJson({'error': 'User ID required'});
}
// Check ownership or admin role
if (!req.isAuthenticated || (req.userId != userId && !req.hasRole('admin'))) {
return res.forbidden().sendJson({'error': 'Access denied'});
}
try {
final validatedData = await req.validate({
'name': 'optional|min:2|max:100',
'email': 'optional|email',
'bio': 'optional|max:500',
'avatar': 'optional|file|max:2MB|mimes:jpeg,png,gif',
});
// Handle avatar upload
if (req.hasFile('avatar')) {
final avatarFile = req.file('avatar')!;
final filename = 'avatar_${DateTime.now().millisecondsSinceEpoch}.${avatarFile.extension}';
await avatarFile.saveTo('storage/avatars/$filename');
validatedData['avatar'] = filename;
}
final user = await User.find(userId);
await user.update(validatedData);
req.flashSession('success', 'Profile updated successfully');
res.sendJson({
'user': user.toJson(),
'message': 'Profile updated successfully'
});
} catch (e) {
res.badRequest().sendJson({
'error': 'Validation failed',
'message': 'Please check your input'
});
}
}
static Future destroy(Request req, Response res) async {
final userId = req.param('id');
if (userId == null) {
return res.badRequest().sendJson({'error': 'User ID required'});
}
// Only admins can delete users
if (!req.hasRole('admin')) {
return res.forbidden().sendJson({'error': 'Admin access required'});
}
final user = await User.find(userId);
if (user == null) {
return res.notFound().sendJson({'error': 'User not found'});
}
await user.delete();
// Clear session if user deleted themselves
if (req.userId == userId) {
req.destroySession();
}
res.sendJson({'message': 'User deleted successfully'});
}
}
// Register routes
void registerUserRoutes(Server server) {
router.group(
prefix: '/api/users',
routes: (router) {
router.get('', UserController.index);
router.post('', UserController.store);
router.get('/:id', UserController.show);
router.put('/:id', UserController.update);
router.delete('/:id', UserController.destroy);
}
);
}Best Practices
Guidelines for effective request handling
Do's
- Always validate input data before processing
- Use appropriate HTTP status codes in responses
- Handle file uploads securely with size limits
- Implement proper authentication checks
- Use session flash messages for user feedback
- Validate CSRF tokens for state-changing operations
Don'ts
- Don't trust user input without validation
- Don't store sensitive data in cookies without encryption
- Don't skip authentication checks on protected routes
- Don't expose error stack traces in production
- Don't allow unlimited file upload sizes
- Don't use GET requests for state-changing operations
