view method
Renders a template and sends HTML response.
- Sets
Content-Type: text/html - Adds caching and security headers
- Sets XSRF token cookie
- Handles errors gracefully in debug mode
Implementation
Future<HttpResponse> view(String template, [ViewData? data]) async {
final engine = App().container.make<TemplateEngine>();
final config = App().container.make<AppConfig>();
final user = await Auth.user(this);
if (user != null) {
final userData = {"user": user.toJson()};
data = {...?data, ...userData};
}
// Ensure CSRF token is available to the view
if (data != null && !data.containsKey('csrf_token') || data == null) {
final csrfCookie = cookies.firstWhereOrNull((cookie) => cookie.name == 'archery_csrf_token');
// If cookie exists, use it. If not, generate new one (which will be set in response below)
final token = csrfCookie?.value ?? App.generateKey();
data = {...?data, 'csrf_token': token};
}
try {
final html = await engine.render(template, data);
// --- Performance headers ---
response.headers.contentType = ContentType.html;
response.headers.set(HttpHeaders.varyHeader, 'Accept-Encoding');
response.headers.set(HttpHeaders.cacheControlHeader, 'no-cache, no-store, must-revalidate, max-age=0');
response.headers.set(HttpHeaders.pragmaHeader, 'no-cache');
response.headers.set(HttpHeaders.expiresHeader, '0');
// --- Security headers ---
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'SAMEORIGIN');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('X-XSS-Protection', '1; mode=block');
final cookie = Cookie('archery_csrf_token', data['csrf_token'] ?? App.generateKey())
..httpOnly = true
..secure = true
..sameSite = SameSite.lax
..path = '/';
final sessions = App().tryMake<List<Session>>();
if (sessions != null && sessions.isNotEmpty) {
final requestCookie = cookies.firstWhereOrNull((cookie) => cookie.name == "archery_guest_session");
if (requestCookie != null) {
final session = sessions.firstWhereOrNull((session) => session.token == requestCookie.value);
if (session != null) {
session.csrf = cookie.value;
}
}
}
return response
..cookies.add(cookie)
..write(html)
..close();
} catch (e, stack) {
if (config.get('app.debug')) {
return response
..statusCode = HttpStatus.internalServerError
..write("$e\n\n$stack")
..close();
}
return response
..statusCode = HttpStatus.internalServerError
..write(e)
..close();
}
}