acsiomatic/http-payload-bundle

dev-main 2023-04-07 16:46 UTC

This package is auto-updated.

Last update: 2024-04-07 19:02:58 UTC


README

The acsiomatic/http-payload-bundle handles HTTP payload within Routes that behave like an API Endpoint in Symfony applications.

Features In Short

MapRequestBody is a controller argument attribute which:

  1. transforms incoming HTTP Request Body into an Object
  2. validates the object using Symfony Validation
  3. injects the object into the Route argument

MapUploadedFile is a controller argument attribute which:

  1. extracts an UploadedFile object from incoming HTTP Request
  2. validates the object using File Constraints
  3. injects the object into the Route argument

ResponseBody is a route attribute which:

  1. looks for a suitable response format through Content Negotiation
  2. serializes the data returned by the dispatched route
  3. exceptions thrown after the kernel.controller event are also serialized
  4. injects the serialized data into the Response object

Installing

composer require acsiomatic/http-payload-bundle

Configuration

# config/packages/acsiomatic_http_payload.yaml

acsiomatic_http_payload:
    request_body:
        default:
            formats: ['json']
            deserialization_context: []
            validation_groups: ['Default']
    file_upload:
        default:
            constraints: []
    response_body:
        default:
            formats: ['json']
            serialization_context: []

Use Cases

Receiving Objects

The MapRequestBody attribute injects the HTTP Request Body into a Route argument. Incoming data is deserialized and validated before being injected.

# src/Controller/LuckyController.php

namespace App\Controller;

use Acsiomatic\HttpPayloadBundle\RequestBody\Attribute\MapRequestBody;
use App\NumberRange;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;

#[AsController]
final class LuckyController
{
    #[Route('/lucky/number', methods: ['GET'])]
    public function number(
        #[MapRequestBody] NumberRange $range,
    ): Response {
        return new Response(
            (string) random_int($range->min, $range->max)
        );
    }
}
# src/NumberRange.php

namespace App;

use Symfony\Component\Validator\Constraints as Assert;

final class NumberRange
{
    #[Assert\GreaterThanOrEqual(0)]
    public int $min;

    #[Assert\GreaterThanOrEqual(propertyPath: 'min')]
    public int $max;
}

Receiving Files

The MapUploadedFile attribute fetches the file from the Request and applies custom constraints before injecting it into the Route argument.

# src/Controller/UserController.php

namespace App\Controller;

use Acsiomatic\HttpPayloadBundle\FileUpload\Attribute\MapUploadedFile;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\File;

#[AsController]
final class UserController
{
    #[Route('/user/picture', methods: ['PUT'])]
    public function picture(
        #[MapUploadedFile(
            constraints: new File(mimeTypes: ['image/png', 'image/jpeg']),
        )] UploadedFile $picture,
    ): Response {
        return new Response('Your picture was updated');
    }
}

Returning Objects

The ResponseBody attribute serializes the object returned by the Route and fills the Response content with it.

# src/Controller/CelebritiesController.php

namespace App\Controller;

use Acsiomatic\HttpPayloadBundle\ResponseBody\Attribute\ResponseBody;
use App\Person;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;

#[AsController]
final class CelebritiesController
{
    #[Route('/celebrities/einstein', methods: ['GET'])]
    #[ResponseBody]
    public function einstein(): Person
    {
        $person = new Person();
        $person->name = 'Albert Einstein';
        $person->birthdate = new \DateTimeImmutable('1879-03-14');

        return $person;
    }
}
# src/Person.php

namespace App;

use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;

final class Person
{
    public string $name;

    #[Serializer\Context([DateTimeNormalizer::FORMAT_KEY => \DateTimeInterface::ATOM])]
    public \DateTimeImmutable $birthdate;
}

Returning Files

See Streaming File Responses.