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>();
thisSession?.user = await Auth.user(this);
final session = {"session": thisSession?.toJson()};
data = {...?data, ...session};
// Ensure CSRF token is available to the view
String token;
bool isNewToken = false;
if (data.containsKey('csrf_token')) {
token = data['csrf_token'];
} else {
final csrfCookie = cookies.firstWhereOrNull(
(cookie) => cookie.name == 'archery_csrf_token',
);
if (csrfCookie != null) {
token = csrfCookie.value;
} else {
token = App.generateKey();
isNewToken = true;
}
data = {...data, 'csrf_token': token};
}
try {
final html = await engine.render(template, data);
// --- Performance & Security headers ---
response.headers.contentType = ContentType.html;
_viewHeaders.forEach((key, value) {
response.headers.set(key, value);
});
if (isNewToken) {
final cookie = Cookie('archery_csrf_token', token)
..httpOnly = true
..secure = true
..sameSite = SameSite.lax
..path = '/';
response.cookies.add(cookie);
}
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 = token;
}
}
}
return response
..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();
}
}