Adding extra new parameter to header as response in symfony2 - symfony

I want to send response to the client which should include some details in header which is common, let say userID and others datails in the body. How to add such new parameters to header of response,
I tried,
public function postAPIAction()
{
$jsonData = $this->getRequest()->getContent();
$decodePostRequest = json_decode($jsonData, true);
// processing is involved........
$uniqueKey=$this->generateUniqueKey();
$response = new Response();
$response->headers->add(array('userId' => $uniqueKey));
return new Response(json_encode(array('errorcode' => '1'), true));
}
which is not working.

You have to return the response you've set the headers on instead of creating a new one in the return statement.

You're creating a new response in your return.
You should use the response you created before.
$response = new Response();
$response->headers->add(array('userId' => $uniqueKey));
$response->setContent(...);
return $response;

Related

Forward BinaryFileResponse between two Symfony2 apps

I have two Symfony apps (APIs) talking to each other via HTTP requests/responses using cURL PHP function. This works fine when they get small JSON responses, but the problem comes when getting and serving files. API1 (exposed to the Internet) needs to serve a file that is only accessible by API2(private, connected to API1 via VPN).
If I encode the content of the file in the first API and then pass it in the response body there is no problem, I can reconvert the file back to a stream and serve in the first API as a BinaryFileResponse. The problem comes with big files (>30MB), where the response body is huge and symfony's is not able to allocate that much memory.
Is there a way to forward or redirect a BinaryFileResponse from one API to the other, so the middle layer is invisible for the client?
These are the two pieces of code in each application:
Public API:
/**
*
* #Get("/api/login/getfile")
*/
public function testGetFilePrivate(Request $request)
{
$url = 'private/variantset/9/getfile';
$url = $this->container->getParameter('api2_url').$url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 300000); //Set timeout in ms
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, TRUE);
$fileContent = base64_decode($data['filedata']);
$response = new Response($fileContent);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$data['filename']
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
Private API:
/**
* #Get("/api/private/variantset/{id}/getfile")
*/
public function getVariantsetDataFileById($id)
{
$variantset = $this->getVariantsetById($id);
if(!$variantset){
$response = array("getdata"=>"ko","error"=>"Variantset does not exists");
return new JsonResponse($response);
}
if($variantset->getCreated()){
$auth_variants_dir = $this->container->getParameter('auth_variants_path');
$file_path = $auth_variants_dir . '/' . $variantset->getDatafile() . '.gpg';
$data = [
"getdata"=>"ok",
"filename" => $variantset->getDatafile() . '.gpg',
"filedata" => base64_encode(file_get_contents($file_path))
];
$response = new Response();
$response->setContent($this->container->get('serializer')->serialize($data, 'json'));
$response->headers->set('Content-Type', 'application/json');
}else{
$response = new JsonResponse(array("getdata"=>"ko","error"=>"Variantset does not have a file yet"));
}
return $response;
}
Finally found the solution by combining the answers in
Streaming a large file using PHP
and
Manipulate a string that is 30 million characters long
Instead of using cURL PHP function, the HTTP stream wrapper is used to catch API2 response. This wrapperThe output is then parsed by using Symfony's StreamedResponse Class:
$response = new StreamedResponse(function() use($url) {
$handle = fopen($url, 'r');
while (!feof($handle)) {
$buffer = fread($handle, 1024);
echo $buffer;
flush();
}
fclose($handle);
});
$response->headers->set('Content-Type', 'application/octet-stream');
return $response;
I am still struggling on how to the content-type from the initial request, I will edit the response if I finally manage to get it properly. Any suggestions are welcomed.

Controller unit test in slim3

At the outset, I would like to say - I'm new in unit testing in PHP (phpunit).
In my new project (slim3 framework) I would like to test my controllers for example LoginController.
My idea is (in unit test method)
Create instance of LoginController
Mock some services in controller (DI)
Execute method which is response for request (in my controllers method __invoke)
My problem is about parameters for __invoke method.
In Slim3 callable method for request has two first params:
RequestInterface $request and ResponseInterface $response
How can I create this parameters in my unit test class? I was searching for some examples for this issue but without success.
Any suggestions?
I've found some code in Slim3 tests to mock request:
protected function requestFactory()
{
$uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123');
$headers = new Headers();
$cookies = array(
'user' => 'john',
'id' => '123',
);
$env = Slim\Http\Environment::mock();
$serverParams = $env->all();
$body = new Body(fopen('php://temp', 'r+'));
$request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body);
return $request;
}
But I'm not sure that is good way.
Thanks for any help
I wrote up one solution here: https://akrabat.com/testing-slim-framework-actions/
I use Environment::mock() to create a $request and then I can run the action. Making each route callable a class where all dependencies are injected into the constructor makes this all much easier too.
Essentially, a test looks like this:
class EchoActionTest extends \PHPUnit_Framework_TestCase
{
public function testGetRequestReturnsEcho()
{
// instantiate action
$action = new \App\Action\EchoAction();
// We need a request and response object to invoke the action
$environment = \Slim\Http\Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'QUERY_STRING'=>'foo=bar']
);
$request = \Slim\Http\Request::createFromEnvironment($environment);
$response = new \Slim\Http\Response();
// run the controller action and test it
$response = $action($request, $response, []);
$this->assertSame((string)$response->getBody(), '{"foo":"bar"}');
}
}

Symfony2 Doctrine - Flushing in kernel.response listener flushs bad data

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.

Symfony2 request called twice

I have a problem with Symfony2 duplicating requests.
The example controller below should just return a transparent gif. However, when the URL is requested there are two 'hits' to the request and doSomething() is called twice instead of once.
// simple action in a controller, ends up being called twice
public function testAction() {
doSomething();
$response = new Response(base64_decode('R0lGODlhAQABAIAAAOrq6gAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='));
$response->headers->set('Content-Type', 'image/gif');
return $response;
}
If I change the response line so it sets a blank response then this stops happening (i.e. doSomething() is only called once):
// with a blank response it is only called once
public function testAction() {
doSomething();
$response = new Response('');
$response->headers->set('Content-Type', 'image/gif');
return $response;
}
If I remove the headers line then it's only called once:
// without the Content-Type header it is only called once
public function testAction() {
doSomething();
$response = new Response(base64_decode('R0lGODlhAQABAIAAAOrq6gAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='));
return $response;
}
Finally (here's the punchline)... if I change the Content-Type to text/html (or text/css) then it is only called once!
// if a text content type is used, it is only called once
public function testAction() {
doSomething();
$response = new Response(base64_decode('R0lGODlhAQABAIAAAOrq6gAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='));
$response->headers->set('Content-Type', 'text/html');
return $response;
}
But with real content and the proper headers it is called twice (and two identical looking entries appear in the web profiler). The same problem also happens if a php template is used to render the content (I haven't tried with twig).
What could be causing this and how can it be prevented (or worked around)?
Edit: So, in summary, it was a Firefox bug and nothing to do with Symfony at all.

form->bind($request) during ajax call outputs an array

I am posting a form with Ajax.
I have a surprising response when binding a form within an Ajax call:
public function newCartAjaxAction(Request $request)
{
$form = $this->container->get('new_cart_form.factory')->create();
$formHandler = $this->container->get('new_cart_form.handler');
if ('POST' === $request->getMethod())
{
$form->bind($request);
if ($form->isValid())
{
$formHandler->processValidForm($form);
$response = new Response();
$response->headers->set('Content-type', 'application/json; charset=utf-8');
$response->setContent(json_encode('hello'));
return $response;
}
//...
}
//....
}
Using firebug, I surprisingly obtain 3 outputs in the ajax Response:
array(2) {[0]=>int(3)[1]=>int(5)} //unexpected response
int(3) //unexpected response
"hello" //The only response needed
After debugging, I figured out that output 1 and 2 are from $form->bind($request);
Does anyone know why that is? I am very surprised to obtain a response from the form binding step as the only response that I am supposed to send is $response...
Have I done something wrong?
It's likely that these outputs were caused by code you've written.
First, make sure that your vendors are clean by reinstalling them.
Then, it could also be a form event listener/subscriber that you've written, so have also a look in this way.

Resources