How to set status code from service layer in Symfony 2? - symfony

I have a service that is helping me to validate some forms. I want to set HTTP status code from inside of it when it finds that form is actually invalid.
Is it OK to do so? How do I do it? Thanks!

The way I would do that (probably there's lot more solutions) is throwing some (custom) exception in your service in case your form is invalid.
Then I would create an exception listener which would listen on kernel exception event. Configuration of such listener would look something like this (services.yml)
kernel.listener.your_listener:
class: Acme\Your\Namespace\YourExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onException }
And than your onException method in YourExceptionListener class:
public function onException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
if ($exception instanceof YourCustomExceptionInterface) {
$response = new Response('', 400);
$event->setResponse($response);
}
}
This way you get nicely decoupled stuff, and your form validation service is independent of response.

Related

Symfony: redirecting to homepage after encountering an error

I've recently started learning Symfony, and I've been trying to make an app that will redirect user to the homepage after encountering an error (For the sake of the question, it can be error 404) However, I had problems with finding a way to do so.
Before, I used TwigErrorRenderer as described in Symfony documentation to handle my errors, but it only explains how to redirect to new error pages created by myself. Could somebody help me with this issue?
It is generally not a good idea to do this, because you want to tell the user that their request was not processed due to an error, or that they accessed non-existing page.
But if you really want to, you can achieve it with this Event Listener.
// src/EventListener/ExceptionListener.php
<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\Routing\RouterInterface;
final class ExceptionListener
{
private RouterInterface $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function onKernelException(ExceptionEvent $event): void
{
// You should log the exception via Logger
// You can access exception object via $event->getThrowable();
$homepageRoute = $this->router->generate('homepage', [], RouterInterface::ABSOLUTE_URL);
$response = new RedirectResponse($homepageRoute);
$event->setResponse($response);
}
}
You also need to register the Event Listener in your services.yaml.
services:
App\EventListener\ExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.exception }
Please note the following:
The Event Listener assumes that your Homepage route is called homepage;
you really should log the exception or you will lose logs about all of them;
as stated at the top of this answer, this is not a good approach to deal with exceptions.

Misunderstanding of event.listener to check user device

I have set up a simple listener and service to check if the user is using a mobile device. I will gladly share to you my research to help others who don't understand quite easily this method.
My goal is to know if my user is using a mobile device or not at first
however I'm stuck in how to manipulate this function, I really had a hard time understanding how that works.
Here is my code
in my service.yml
template.loader:
class: ST\BackofficeBundle\EventListener\DeviceListener
tags:
- { name: kernel.event_listener, event: kernel.exception }
My even listener I created to check if the user is using a mobile
class DeviceListener
{
public function onKernelView(getResponseForExceptionEvent $event)
{
$event->getRequest()->getSession()->set('mobile', true);
$response = new Response();
$response->setContent($event);
$event->setResponse($response);
}
}
Is there any line missing that I should do here or in a controller?
i saw there is this line in the symfony doc $_SERVER ($request->headers->get('User-Agent')) should I use it somewhere in my code?
thank you
If you want to modify your response according to User-Agent then I would create a kernel.response listener.
Therefore in your services.yml declare something like
template.loader:
class: ST\BackofficeBundle\EventListener\DeviceListener
tags:
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
This way your listener will be called just before sending response. Of course you can modify your response as you need:
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
$request = $event->getRequest();
if ($request->headers->get('User-Agent') == 'whatever') {
$response->setContent('hello');
}
$event->setResponse($response);
}
For more detailed informations about kernel events, take a look at this page.

Symfony2 exception event listener run some controller actions

I need to do one of two things (in priority order, but just need one of them).
All of this is to be done inside a function that runs as an Exception Event Listener (http://api.symfony.com/2.2/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.html).
Both below are totally easy inside a normal controller, but I can't see it possible inside an event listener function.
(1) Run a controller as normal and output as normal as though that route had been executed:
e.g. $event->runController('controllerName');
(2) render a template as normal using the same syntax as would inside a normal controller:
return $this->render('Bundle:Default:feedback.html.twig', array([template vars]));
take a look at symfony's default exception listener in Symfony\Component\HttpKernel\EventListener\ExceptionListener.
(1) running a controller can be realized this way:
$request = new \Symfony\Component\HttpFoundation\Request();
$request->setMethod('GET');
[..]
try {
$response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, true);
} catch (\Exception $e) {
return; // pass it to next exception listener this way
}
$event->setResponse($response);
(2) rendering a template in exception listener
all you need to do is pass templating (engine) to the listener inside the service.yml
services:
foobar.exception_listener_service:
class: %foobar.exception_listener_service.class%
arguments:
container: "#service_container"
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 255 }
inside the listener you can render templates as listed below
$templating = $this->container->get('templating');
$response = new Response($templating->render('foobar:Exception:error404.html.twig', array('exception' => $exception)));
$event->setResponse($response);

Redirect to different action depending on User Status

I'm working with Symfony 2.1. I want to setup a redirect routine depending on User is logged in or not. The way I check it is $User->isLoggedIn() where User is a service.
I want to do this before a controller executes. I have few other things happening just before Controller executes. I use event: kernel.controller of kernel.event_listener to do those things. But I realized that I can not redirect to a URL using this event.
I understand I need to use event: kernel.request of kernel.event_listener to be able to redirect to a URL.
Problem.
I use the following logic to figure out whether I need to redirect or not.
if (!$controller[0] instanceof NoLogInNeededInterface) {
if (!$User->isLoggedIn()) {
//redirect here
}
}
So, in the kernel.request event, $controler[0] is not available. In the kernel.controller event, response can't be set (will be ignored).
Has anyone got same problem and solved. Or is there any better way of doing, what I'm trying to do?
I realized that what I wanted could be achieved by using kernel.exception event of kernel.event_listner.
So in Services:
my.service:
class: MyBundle\EventListner\ExceptionListner
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
arguments: [ %logoutUrl% ]
Then in the Class itself:
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
class ExceptionListner
{
protected $_url;
public function __construct($url)
{
$this->_url = $url;
}
public function onKernelException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
//dont necessarily need this condition
if ($exception instanceof RedirectException) {
$response = new RedirectResponse($this->_url);
$event->setResponse($response);
}
}
}
I'm still up for suggestion, if this is or is not better approach.
Hope this will help someone who is struggling as well.
P

Is there a symfony2 event/handler for session timeout a.k.a not logged in

Im using the FOSRestBundle to make ajax json calls within a Firewall. Everything seems to be working great, with the exception that Im not able to to handle when a session timeout has occurred. Right now it's redirecting to login_check in this scenario, returning html rather than json to the client.
Im aware, and use success_handler and failure_handler's within my app. I cannot find a built in handler for dealing with authorisation failures, such as session timeout.
Is there something within the FOSRestBundle that can help address this, or something Im not seeing within Symfony2?
Yes, Symfony offers the possibility to handle exceptions. You have to create an event listener which observes the kernel.exception event with a high priority. Create an event handler like this:
<?php
namespace Acme\Bundle\MyBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class AjaxAuthenticationListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
$request = $event->getRequest();
$format = $request->getRequestFormat();
$exception = $event->getException();
if ('json' !== $format || (!$exception instanceof AuthenticationException && !$exception instanceof AccessDeniedException)) {
return;
}
$response = new JsonResponse($this->translator->trans($exception->getMessage()), $exception->getCode());
$event->setResponse($response);
$event->stopPropagation();
}
}
Now you have to register your event handler in one of your service.yml's, like this:
kernel.listener.ajax_authentication_listener:
class: Acme\Bundle\MyBundle\EventListener\AjaxAuthenticationListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 250 }
Note the priority parameter, used to tell Symfony to execute the handler before its own handlers, which have a lower priority.
And on your frontend you can register an event handler for jQuery, which reloads the page on such an error.
$(document).ready(function() {
$(document).ajaxError(function (event, jqXHR) {
if (403 === jqXHR.status) {
window.location.reload();
}
});
});
See this gist for reference.
I'm not sure if there's anything explicitly inside the FOSRest bundle, but Symfony2 itself is able to handle session timeouts.
Have you tried looking here? http://symfony.com/doc/current/components/http_foundation/session_configuration.html

Resources