When you build an API, the first line of defense isn't your firewall or your database — it's the request itself. Every payload that hits your controller is untrusted until proven otherwise. Yet a surprising number of production APIs still rely on manual json_decode() calls and scattered if checks, which is fragile, hard to maintain, and easy to bypass.
This article walks through the modern, "senior-level" approach to request validation in Symfony 7, and clarifies a point that's often misunderstood: where XSS protection actually belongs.
The Symfony Way: DTO + Validator + MapRequestPayload
Since Symfony 6.3, the #[MapRequestPayload] attribute lets you deserialize and validate a JSON body in one step, using a dedicated Data Transfer Object
Step 1 — Define the shape of the request:
// src/Dto/UpdateCartItemRequest.php
use Symfony\Component\Validator\Constraints as Assert;
final class UpdateCartItemRequest
{
#[Assert\NotNull]
#[Assert\Type('integer')]
#[Assert\Range(min: 0, max: 9999)]
public ?int $quantity = null;
}
Step 2 — Let Symfony handle parsing and validation:
use App\Dto\UpdateCartItemRequest;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
#[Route('/items/{productId}', methods: ['PATCH'])]
public function updateItem(
int $productId,
#[MapRequestPayload] UpdateCartItemRequest $dto,
): JsonResponse {
$cart->setQuantityFor($productId, $dto->quantity);
$this->em->flush();
return $this->json($this->hydrate($cart));
}
Top comments (0)