11

So the question is pretty straightforward. I have a code in my controller that has became redundant, and i've decided to make the annotation for it.

if (!$request->getContentType() === 'json' ) {
    return new JsonResponse(array('success' => false));
}
$content = $request->getContent();

if(empty($content)){
    throw new BadRequestHttpException("Content is empty");
}
$data = json_decode($content, true);
if(empty($data) || !array_key_exists('type', $data)) {
    return new JsonResponse(array('success' => false));
}

How do i make custom annotation @CheckRequest in which I can use the $request object as a parameter?

2 Answers 2

10

You need to make a custom annotation and then a listener that injects the annotation reader and handles the kernel.controller event:

Annotation

/**
 * @Annotation
 */
class CheckRequest
{
}

Service Definition

services:
    controller_check_request:
        class: AppBundle\EventListener\ControllerCheckRequestListener
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController}
        arguments:
            - "@annotation_reader"

Listener:

namespace AppBundle\EventListener;

use AppBundle\Annotation\CheckRequest;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class ControllerCheckRequestListener
{
    /** @var Reader */
    private $reader;

    /**
     * @param Reader $reader
     */
    public function __construct(Reader $reader)
    {
        $this->reader = $reader;
    }

    /**
     * {@inheritdoc}
     */
    public function onKernelController(FilterControllerEvent $event)
    {
        if (!is_array($controllers = $event->getController())) {
            return;
        }

        $request = $event->getRequest();
        $content = $request->getContent();

        list($controller, $methodName) = $controllers;

        $reflectionClass = new \ReflectionClass($controller);
        $classAnnotation = $this->reader
            ->getClassAnnotation($reflectionClass, CheckRequest::class);

        $reflectionObject = new \ReflectionObject($controller);
        $reflectionMethod = $reflectionObject->getMethod($methodName);
        $methodAnnotation = $this->reader
            ->getMethodAnnotation($reflectionMethod, CheckRequest::class);

        if (!($classAnnotation || $methodAnnotation)) {
            return;
        }

        if ($request->getContentType() !== 'json' ) {
            return $event->setController(
                function() {
                    return new JsonResponse(['success' => false]);
                }
            );
        }

        if (empty($content)) {
            throw new BadRequestHttpException('Content is empty');
        }

        $data = json_decode($content, true);

        if ($request->getContentType() !== 'json' ) {
            return $event->setController(
                function() {
                    return new JsonResponse(['success' => false]);
                }
            );
        }
    }
}

Notice that instead of returning the response, you set the entire controller with $event->setController();, and you also must return when making that call.

Then in your controller you can set it on the entire class:

use AppBundle\Annotation\CheckRequest;

/**
 * @CheckRequest
 */
class YourController extends Controller
{
}

or individual methods/actions:

use AppBundle\Annotation\CheckRequest;

class TestController extends Controller
{
    /**
     * @Route("/", name="index")
     * @CheckRequest
     */
    public function indexAction(Request $request)
    {
        // ...
    }
}
Sign up to request clarification or add additional context in comments.

8 Comments

Keep in mind I haven't specifically tested this so if you discover a small bug or something, let me know and I'll edit the post.
Hey Jason, thank for the quick answer. I found a small bug. It seems like the FilterControllerEvent does not have setResponse method.
Ah, right. I updated the function call, I think it requires setController() instead. Give that a shot.
Also, one more thing, i think you missunderstood my intention. I wasn't going to use annotation on entire controller, only on some methods. So i think that it should go something like this: $object = new \ReflectionObject($controller[0]);// get controller $method = $object->getMethod($controller[1]); And then foreach of those methods i need to extract the annotations via this->reader->getMethodAnnotations($method)... Thanks for the answers, i appreciate it alot. :)
I updated the answer - it should work and allow you to specify it either for the entire controller class or a specific controller action now.
|
1

For Symfony 3.4.*

public function onKernelController(FilterControllerEvent $event){

    if (!is_array($controllers = $event->getController())) {
        return;
    }

    list($controller, $methodName) = $controllers;

    $reflectionClass = new \ReflectionClass($controller);

    // Controller
    $reader = new \Doctrine\Common\Annotations\AnnotationReader();
    $classAnnotation = $reader->getClassAnnotation($reflectionClass, AnnotationClass::class);

    // Method
    $reflectionMethod = $reflectionClass->getMethod($methodName);
    $methodAnnotation = $reader->getMethodAnnotation($reflectionMethod, AnnotationClass::class);

    if(!($classAnnotation || $methodAnnotation)){
        return;
    }

    /** TODO CODE HERE **/
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.