POST variables not seen in Symfony's 3.2 Controller - php

Debugger shows, that there is POST data in request, but I cannot get it by $request->get('foo');
And $request->request->all(); gives array [0].
My Ajax POST request (by AngularJS):
...
$http.post('http://admin/about-company/setJSONObj',
{foo: 'bar'}
);
My controller with debugging info in comments (Symfony 3.2.9):
use Symfony\Component\HttpFoundation\Request;
...
public function updateAction(Request $request)
{
$foo = $request->get('foo'); // null
$requestType = $request->getContentType(); // json
$content = $request->getContent(); // {"foo":"bar"}
I used these approach on Symfony 2.7 project, and it worked fine, but I'm not sure, what's happening in these case?
Also, maybe there is any Symfony framework config variable that tells not to parse POST data, or hides it during caching the request?

For POST request is:
$request->request->get('foo');

Try using FormData for your client-side ajax's call
For example : try something like
var formData = new FormData();
formData.append('foo', 'bar')
$http.post('http://url',
formData
);
Ok, I wasn't paying attention that you used json so,
You wont get the content of $foo in request but you need to json_decode the $content
So by keeping the same way you sent data :
$http.post('http://admin/about-company/setJSONObj',
{foo: 'bar'}
);
You just have to call
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
...
/**
* #Route("/setJSONObj", name="admin_pages_set_ajax_obj")
* #Method("POST")
*/
public function updateAction(Request $request)
{
$foo = $request->get('foo'); // null
$requestType = $request->getContentType(); // json
$content = $request->getContent(); // {"foo":"bar"}
$data = json_decode($content, true);
dump($data['foo']); // "foo"
//And you can know replace the data of the request. So
$request->request->replace($data);
My guess is because symfony need have the 'Content-Type' 'application/x-www-form-urlencoded' but Angular by default have a application/json (so you can override the headers on the $http.post call.

It turned out, my mistake was to expect POST variables in $request->request->all(); while posting JSON: see explanation https://www.toptal.com/....
For creating REST api with Symfony it's normal to use FOSRestBundle: see body listener. So, it solves my problem in elegant way.
Also, #henrily suggested a workaroud somebody can use, but it's just a workaround.

Related

Symfony-Giving all api responses according to content type

routes.yaml
Whatever the content type is, I want the response to be of that type. How can I do it?
I want it to be response, by content type. I want to do it globally. For example; If the content type is json, the response must be json. If the content type is XML, the response should return XML. or if the content type is xxx the response should return xxx
The system itself should automatically determine the return type according to the content-type. Is this possible in symfony? Because Symfony returns exceptions in xml format by default. If the api user sends the content-type as json and receives an exception, it receives it as xml. I dont want this.
I can write _format json or xml. But I want it to be dynamic
Check how HttpKernel is handling response.
To have response type dynamically respond to client sent Accept header you must write some kind of mapper for it.
You should write some subscriber/listener to transform your response to needed format:
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
final class ResponseSubscriber implements EventSubscriberInterface
{
public function __contstruct(private Request $request) {}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => 'onResponse',
];
}
public function onResponse(ResponseEvent $event): void
{
// Skip on internal requests. E.g. redirect
if (!$event->isMainRequest()) {
return;
}
$data = $event->getResponse();
$requestedType = $this->request->headers->has('Accept')
? $this->request->headers->get('Accept')
: 'application/json';
$response = match ($requestedType) {
'application/json' => $this->getJsonResponse($data),
'application/xml' => $this->getXmlResponse($data),
'text/html' => $this->getHtmlResponse($data),
...
default => throw new \InternalException('Request type ' . $requestedType . ' is not supported'), // This better be checked on KernelEvents::REQUEST
}
$event->stopPropagation();
$response->setResponse($response);
}
}
You can use XDebug to do step-debugging and see where your response data is travelling for better understanding of Symfony internals.

gcloud function php post size issue

I have a gcloud function in PHP that receives a json object.
Inside my function, I read the json object like this: file_get_contents('php://input').
This works for me when the json object that I receive in the function is less than 16KB, but when the json object is greater than 16KB it returns empty.
There is no code, neither client- nor server-side ...
You might be sending as enctype="multipart/form-data"
? https://php.net/manual/en/wrappers.php.php#wrappers.php.input
While file_get_contents('php://input') is questionable by itself.
Instead use the default method signature, which provides ServerRequestInterface $request:
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
...
public static function on_https( ServerRequestInterface $request ): ResponseInterface {
$params = $request->getQueryParams();
$body = $request->getBody();
$body->rewind(); // or $body->seek(0);
$bodyText = $body->getContents();
$data = json_decode($bodyText);
return (new Response())
->withHeader('Content-Type', 'application/json; charset=utf-8')
->withBody(Utils::streamFor(json_encode($data)))
->withStatus(200);
}
Just recently pushed a template composer project to GitHub, based on the documentation. Not exactly certain about $body->rewind(), that's only required for streams (it may return string already). This requires "guzzlehttp/psr7": "^2.1.0". Also see: What is PSR-7 and how to use it?

How to pass parameter using laravel's Request?

Currently we're doing unit testing in Laravel, and I just noticed my colleague this line below (its working fine though). I look for a documentation in Laravel about this but I can't find it. It seems all we're just focusing on getting the request input values in the documentation.
use Illuminate\Http\Request;
// ...more code here
$request = Request::create('/users/all', 'GET');
I just wanna ask how to pass a parameter using the above code line? And someone can give me a documentation about that.
Check the create function at here:
https://github.com/symfony/symfony/blob/5cfe73d95419bac1ffdddc4603db7266e428b454/src/Symfony/Component/HttpFoundation/Request.php#L336
As you can see, you can pass parameteres as third argument:
Example:
Request::create('/users/all', 'GET', ['username' => 'admin']);
Note: Laravel Request extends Symfony Base Request class
The 3rd argument to create is for an array of parameters. Illuminate\Http\Request extends Symfony\Component\HttpFoundation\Request which defines the create method:
public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null)

Difficulty in receiving POST parameter in symfony using angular 4

I am using symfony with angular 4. Both are different projects. In symfony I am returning a post variable which is sent via angular. I am just returning the value. Nothing other than that.
But it's returning me blank value. But when I am testing that symfony url using postman it's returning the correct value.
Symfony code
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers:Content-Type,Accept');
return new Response("a-".$request->request->get('roll')."-b"); //gives '**a- -b**' in console. but from postman gives `a-2-b`
Angular code
this.http.post("http://localhost:8000/user/login",{roll:2}).subscribe(
(data) => console.log(data)
);
Your postman's POST request is different than the Angular's POST request
Maybe in Angular you have to add the content-type: application/json header.
To debugging I advice you to compare the two request with logging in symfony code
I'm not 100% sure but I believe you have to JSON.stringify your object on the angular side:
this.http.post("http://localhost:8000/user/login",JSON.stringify({roll:2})).subscribe(
(data) => console.log(data)
);
Next, as said in comments above. Symfony handles x-www-form-urlencoded body by default. For JSON requests you will need to parse it yourself.
e.g.:
$data = json_decode($request->getContent(), true));
Now doing this in every controller action may seem tedious and you'll loose the Symfony Parameter bag.
You can solve this by making a Request listener:
e.g.:
class JsonRequestEventListener
{
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (empty($request->getContent()))
{
return;
}
if (!$this->isJsonRequest($request))
{
return;
}
if (!$this->transformJsonBody($request))
{
$response = Response::create('Unable to parse request.', 400);
$event->setResponse($response);
}
}
private function isJsonRequest(Request $request)
{
return 'json' === $request->getContentType();
}
private function transformJsonBody(Request $request)
{
$content = $request->getContent();
if ($content === '')
{
return true;
}
$data = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE)
{
return false;
}
if ($data === null)
{
return true;
}
$request->request->replace($data);
return true;
}
}
What this listener does is the following:
When a request comes in, it checks if there is any content and if the request is json. Then it tries to json_decode the request and inserts it in the request parameter bag. ($request->request ...)
The next thing you will have to do is register this listener:
e.g.:
jsonrequest.listener:
class: AppBundle\EventListener\JsonRequestEventListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 100 }
And from now on, if your request is of type application/json and it contains valid json, you can find it's values in the Request Parameter bag:
public function someAction(Request $request)
{
$request->request->all() //holds the json
}
As I can see from your sample, you set the CORS headers in your controller as well. You could in the same way write a CORS listener to handle this for you.

Sending a POST request with PHPUnit

I have a symfony website, and Im trying to do some unit testing. I have this kind of test where I try to submit something:
<?php
namespace Acme\AcmeBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class HomeControllerTest extends WebTestCase {
public function testrandomeThings() {
$client = static::createClient();
$crawler = $client->request(
'POST',
'/',
array(
"shopNumber" => 0099,
"cardNumber" => 231,
"cardPIN" => "adasd"),
array(),
array());
}
but I dont think that the data Im sending is being received in the controler:
class HomeController extends Controller
{
public function indexAction()
{
var_dump($_POST);
die;
return $this->render('AcmeBundle:Home:index.html.twig');
}
}
the var_dump is actually returning me an empty array.
What am I missing to send information through my POST request?
$_POST is a variable filled by PHP and the symfony request is only created from this globals if called directly over http. The symfony crawler doesn't make a real request, it creates a request from the parameters supplied in your $client->request and executes it. You need to access this stuff via the Request object. Never use $_POST, $_GET, etc. directly.
use Symfony\Component\HttpFoundation\Request;
class HomeController extends CoralBaseController
{
public function indexAction(Request $request)
{
var_dump($request->request->all());
die;
return $this->render('CoralWalletBundle:Home:index.html.twig');
}
}
use $request->request->all() to get all POST parameters in an array. To get only a specific parameter you can use $request->request->get('my_param'). If you ever need to acces GET parameters you can use $request->query->get('my_param'), but better set query parameters already in the routing pattern.
I think you're trying to do this:
$client = static::createClient();
$client->request($method, $url, [], [], [], json_encode($content));
$this->assertEquals(
200,
$client->getResponse()
->getStatusCode()
);
You're putting your data (content) in as the params array but you want to put it in as the raw body content which is a JSON encoded string.

Categories