I have a controller that response a json data to another application , this is the controller code :
/**
*
* #Get("/getXXX/{id}")
*/
public function getDataAction($id,Request $request){
$ceService = $this->container->get('comptexpertcews.service');
$employeNumber= $request->get('employeNumber') ;
$url = $this->container->getParameter('serverUri') . $id;
$res = new Response();
$res->setContent($ceService->getCews($url, wsUrl::ws_Headers));
$res->headers->set('Content-TYpe','application/json; charset=UTF-8');
return $res;
}
The problem is by default , if you don't give id in the url , symfony rise exception : not route foundexception , what i want is to handle the exception and personalize with my owner response like sending
{"error" :" id undefined "}
instead of the long message expcetion of symfony
You have two simple options:
Don't use param converter, get you data from a repository and then you can wrap it in try catch and create your own exception/message
If this is something you want to do globally, you can implement an event listener that catches onKernelException event and work with it from there, e.g.:
public function onKernelException(GetResponseForExceptionEvent $event): void
{
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$response = $this->resourceNotFoundResponse(json_encode($exception->getMessage()));
}
if (isset($response)) {
$event->setResponse($response);
}
}
You also need to register you listener as a service, see the documentation here http://symfony.com/doc/current/event_dispatcher.html
Related
To capture the request before the controller I have done:
public function onKernelController (ControllerEvent $ event)
And there I can return an error (401) or do nothing and let it continue to the controller.
$event->getRequest()->attributes->set('id', '33');
I don't know how to pass an object as a parameter to the controller. But the main problem is that the method is executed 2 times with each request, and I don't know why
To validate the token I call a micro service.
Listen to these 2 events on services.yalm:
App\Listener\FirewallTokenController:
tags:
- { name: kernel.event_listener, event: kernel.controller }
App\Listener\FirewallTokenRequest:
tags:
- { name: kernel.event_listener, event: kernel.request }
FirewallTokenRequest runs first. In it you can retrieve the token or tokens and perform the verification. If the token is correct we do nothing and let the execution continue, but if the token is not correct we introduce a custom error that we can recover in FirewallTokenController and do the redirection.
If in FirewallTokenRequest the token is not correct we insert the error:
public function onKernelRequest(RequestEvent $event) {
$request = $event->getRequest();
...
.....
if (badToken){
$request->attributes->set("FirewallTokenEvent", "401");
} else {
//Enter the user or any data that is needed in the Controller
$request->attributes->set("idUser", $idUser);
$request->attributes->set("email", $email);
$request->attributes->set("locale", $locale);
}
}
From FirewallTokenController we look for the error, if we find it we can redirect:
$this->classesExcluded = array("Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController");
public function onKernelController(ControllerEvent $event) {
$controller = $event->getController(); (deprecated)
//In each request this class is executed 2 times, this makes it possible to execute it only once.
if (!is_array($controller) || HttpKernelInterface::MASTER_REQUEST != $event->getRequestType() || in_array(get_class($controller[0]), $this->classesExcluded)) {
return;
}
if ($request->attributes->has('FirewallTokenEvent')) {
$response = intval($request->attributes->get('FirewallTokenEvent'));
if ($response == 401) { //Unathorizated
$redirectUrl = 'unauthorized';
$event->setController(function () use($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
Retrieve the user (or any data) entered in FirewallTokenRequest from the Controller:
$idUser = intval($request->attributes->get('idUser'));
$email = $request->attributes->get('email');
$locale = $request->attributes->get('locale');
I tried to save User gsm data after Authentication Success Event :
/**
* #param AuthenticationSuccessEvent $event
*/
public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event)
{
$data = $event->getData();
$user = $event->getUser();
dd($user);
$gsm = isset(\json_decode($this->requestStack->getCurrentRequest()->getContent())->gsm) ? \json_decode($this->requestStack->getCurrentRequest()->getContent())->gsm : null;
if ($user->getGsm()) { // <------ get user GSM
...
} else {
...
}
$event->setData($data);
}
When i tried to generate token it works fine :
but when i tried to refresh the token
I got symfony error :
Attempted to call an undefined method named "getGsm" of class "Symfony\Component\Security\Core\User\User".
The result of dd($user) is :
You are trying to call a getGsm method on a Symfony User class when you are actually trying to save the GSM data.
Ensure you are working on the correct user and you will need to use setGsm instead.
I'm trying to make use of the new FormSchema class in Silverstripe 4 but I'm having a tough time with the workflow for submitting the form. I've been able to return the schema and state, but when submitting the form back to the controller is where I run in to issues. Here is some example code:
class TestController extends Controller {
private static $allowed_actions = [
'schema',
'TestForm'
];
public function schema(HTTPRequest $request) {
$schema = new FormSchema();
return json_encode($schema->getMultipartSchema(['schema', 'state', 'errors'], "FormID", $this->TestForm()));
}
public function TestForm() {
$fields = FieldList::create(
TextField::create('Name', 'Name'),
EmailField::create('Email', 'Email')
);
$actions = FieldList::create(
FormAction::create('doTestSubmit', 'Submit')
);
$required = RequiredFields::create(['Name', 'Email']);
return Form::create($this, 'TestForm', $fields, $actions, $required);
}
public function doTestSubmit($data, $form) {
return json_encode(array('response' => 'The form validated and was submitted.'));
}
}
So in this scenario hitting /schema returns TestForm schema in json then the front end renders the form. Submitting the form sends the data back to /TestForm where it is validated. If the submission is valid it'll continue to doTestSubmit and return the response. That's great! But, if the submission is not valid then TestForm attempts to return the form and not the schema with the validation messages.
I first though about using a condition in TestForm() like if($this->request->isPOST()) or if($form->validationResult()->isValid()) but it doesn't seem like the proper way to handle it.
Any input or a simple code sample would be great.
I think you cannot use the standard Form > Form Post > Form Action way, you have to create an own Form Handler that validates the form. Like this (untested code):
public function doTestSubmit($request) {
$form = $this->TestForm()
$form->loadDataFrom($request->postVars);
if(!$form->validationResult()->isValid()) {
$formSchema = new FormSchema();
$state = $formSchema->getState($form);
return json_encode($state);
}
//Valid, continue
}
In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.
Thanks for your valuable suggestions
i have created a login system where i want to store the id's of users in session variables
this is my controller for login system
use Symfony\Component\HttpFoundation\Session\Session;
class successController extends Controller
{
public function successAction(Request $request)
{
--some code for form--
$repository = $em->getRepository('RepairStoreBundle:users');
$query = $repository->auth($name,$password);
$error="sorry invalid username or password";
if($query== false)
{
return $this->render('RepairLoginBundle:login:login.html.php', array(
'form' => $form->createView(),'error'=>$error,));
}
else
{
$role=$query[0]['role'];
$id=$query[0]['id'];
if($role == 1)
{
$session = new Session();
$session->start();
$session->set('id',$id);
$result=$repository->display();
return $this->render('RepairLoginBundle:login:success.html.php',array('result'=>$result,));
}
else
{
$session = new Session();
$session->start();
$session->set('id',$id);
$res= $repository->edit($id);
return $this->render('RepairLoginBundle:login:user.html.php',array('res'=>$res));
}
}
}
}
when admin logins with role=1 it will render to success.html.php
in this view how can i get the session variable which i have set in the controller.
i have used $session->get('id');
it is giving me server error please help with this
Upfront Authentication should better be done with the Security Component in Symfony2.
Read more about it in The Book - Security. You should probably also take a look at FOSUserBundle
Accessing the session from a PHP template in symfony2:
echo $app->getSession()->get('whatever');
Session Handling
There is an article in the official documentation:
Components/HttpFoundation - Session Data Management
The API documentation for the Session Component can be found here:
http://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Session.html
In the symfony2 standard-edition you can get the session from within a controller with:
$session = $this->getRequest()->getSession();
As you already have the request as an argument in successAction you could access the session with:
$session = $request->getSession();
Set a value with ( $value needs to be serializable ):
$session->set('key',$value);
Get a value with:
$session->get('key');
Saving (and closing) the session can be done with:
$session->save();
You should also loook at the SessionBag class.
you create a SessionBag and register it with the session. see:
Symfony API
In the registered SessionBag - which implements AttributeBagInterface - you can get and set your key/value's as desired.
TIP: If you want to get the current User and you have a container aware controller ( container injected )
you can use:
$user = $this->container->get('security.context')->getToken()->getUser();
if you are extending Symfony's Controller class in the standard-edition - the shorter way is:
$user = $this->get('security.context')->getToken()->getUser();
or even shorter (Symfony > 2.1.x):
$user = $this->getUser();
Alternative ( If your controller is not container aware ):
Define the controller as a service and inject #security.context:
YAML:
# src/Vendor/YourBundle/Resources/config/services.yml
services:
my.controller.service:
class: Vendor\YourBundle\Controller\successController
arguments: ["#security.context"]
Vendor\YourBundle\Controller\successController:
protected $securityContext;
public function __construct(SecurityContextInterface $securityContext)
{
$this->securityContext = $securityContext;
}
then in your action:
$user = $this->securityContext->getToken()->getUser();
Note:: you have to use the service in your routing aswell if you choose the controller-as-service variant. example routing.yml :
[...]
route_name:
pattern: /success
defaults: { _controller: my.controller.service:successAction }
[...]
[...]
Note... you can also inject the session with "#session"
# src/Vendor/YourBundle/Resources/config/services.yml
[...]
arguments: ["#security.context","#session"]
Note injecting the whole container is resource-heavy. advanced developers inject their needed services one-by-one and not the whole container.
Tip: Normally Controller classes are written with a capital first letter - example: *S*uccessController
General TIP: You have unnecessary dublicate code in your example:
// 'if' and 'else' execute the same stuff here
// result: dublicate code = more code = harder to read
if($role == 1)
{
$session = new Session();
$session->start();
[...]
}
else
{
$session = new Session();
$session->start();
[...]
}
should better be ...
// better: put this stuff before the if/else statement
$session = new Session();
$session->start();
if($role == 1)
{
[...]
}
else
{
[...]
}