view method

Future<HttpResponse> view(
  1. String template, [
  2. ViewData? data
])

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();
  }
}