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

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