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 object
queryQuery parameters map

Header Properties

contentTypeContent-Type header
userAgentUser-Agent header
acceptsJson()Accepts JSON check
isAjax()AJAX request check

Body 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/json
JSON data
application/x-www-form-urlencoded
Form data
multipart/form-data
File 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 bytes

File Methods

saveTo(path)Save to disk
asString()Get as string
sizeFile size in bytes
extensionFile extension

Input 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 value
setSession(key, value)Set value
flashSession(key, value)Flash data
pullSession(key)Get & remove

Flash 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

Need Help?

Join our community for support and discussions

Join Discord Community

On this page