Request & Response

Canvas uses Symfony HttpFoundation for all HTTP handling. Controllers receive a Request object and return a Response object. Both are injected and resolved automatically — no manual instantiation required.

explanation

Injecting the Request

Type-hint Symfony\Component\HttpFoundation\Request in any controller method and Canvas injects the current request automatically:

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class SearchController extends BaseController {

    /**
     * @Route("/search", methods={"GET", "POST"})
     */
    public function search(Request $request): Response {
        $q = $request->query->get('q'); // from URL query string
        // ...
    }
}

Route parameters and the Request object can be combined freely in the same method signature:

/**
 * @Route("/users/{id:int}", methods={"POST"})
 */
public function update(int $id, Request $request): Response {
    $name = $request->request->get('name');
    // ...
}

Reading Request Data

Source Access Example
Query string $request->query->get('key') /search?q=canvas'canvas'
POST body $request->request->get('key') Form field name=Alice
All POST data $request->request->all() Returns associative array
JSON body json_decode($request->getContent(), true) Raw request body as string via getContent()
Headers $request->headers->get('X-Api-Key') Header names are case-insensitive
Cookies $request->cookies->get('key') Read-only; cookies are set on the Response
Uploaded files $request->files->get('field') Returns UploadedFile instance; see Secure File Upload
HTTP method $request->getMethod() Returns 'GET', 'POST', etc.
Method check $request->isMethod('POST') Returns bool
Client IP $request->getClientIp() Respects trusted proxy configuration

Returning Responses

Return any Symfony Response subclass from a controller method. BaseController provides helpers for the most common cases:

// HTML response via template engine
return $this->render('users/index.tpl', ['users' => $users]);

// JSON response
return $this->json(['id' => 1, 'name' => 'Alice']);

// Plain text
return $this->text('OK');

// Redirect
return $this->redirect('/users');

// 404
return $this->notFound('User not found');

// 403
return $this->forbidden('Access denied');

All helpers accept an optional HTTP status code as the last argument. For responses not covered by a helper, instantiate Symfony's classes directly:

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;

return new Response('Created', 201);
return new JsonResponse(['error' => 'Not found'], 404);
return new RedirectResponse('/login', 302);

Session

Canvas does not wrap Symfony's session layer. Access the session directly through the request or inject SessionInterface via the DI container:

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

// Via Request (available anywhere you have the request)
$request->getSession()->set('user_id', $user->getId());
$userId = $request->getSession()->get('user_id');
$request->getSession()->remove('user_id');
$request->getSession()->clear(); // Destroy all session data

// Via constructor injection (Canvas resolves the application-wide instance)
class MyService {
    public function __construct(private SessionInterface $session) {}

    public function doSomething(): void {
        $this->session->set('key', 'value');
    }
}

Cookies

Read cookies from the request and set them on the response:

use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;

public function action(Request $request): Response {
    // Read
    $token = $request->cookies->get('remember_token');

    // Write — attach to the response
    $response = $this->render('dashboard.tpl');
    $response->headers->setCookie(
        Cookie::create('remember_token', $token, strtotime('+30 days'))
    );

    // Delete — expire immediately
    $response->headers->clearCookie('remember_token');

    return $response;
}

Request Access in Aspects

Aspects receive the request through MethodContextInterface. The request can also be replaced, which is how aspects like ValidateAspect and SanitizeAspect pass results downstream via request attributes:

use Quellabs\Canvas\AOP\Contracts\BeforeAspect;
use Quellabs\Canvas\Routing\Contracts\MethodContextInterface;
use Symfony\Component\HttpFoundation\Response;

class StampAspect implements BeforeAspect {

    public function before(MethodContextInterface $context): ?Response {
        $request = $context->getRequest();
        $request->attributes->set('processed_at', microtime(true));
        // No need to call setRequest() — attributes are set by reference
        return null;
    }
}
For the full MethodContextInterface API, see Aspect-Oriented Programming.