skip to Main Content

I’m trying to get POST content in php side (using PHP 8.3, Symfony 7.1)

Here is an example backend:

#[Route("test")]
public function testing(Request $request) {
    $_POST = json_decode($request->getContent(), true);
    if(isset($_POST["line"])) { // FIRST METHOD
        // try when using JS query (see code below)
    }
    if($request->request->get("line") != null) { // SECOND METHOD
        // true when using Symfony queries
    }
    return $this->json([]);
}

Two if are never true at the same time.

  • The first method is multiple times recommended in stackoverflow (like here, here or here), and worked for me since long time
  • The second method is mentionned in Symfony documentation
  1. When using this JS code:
await fetch("/test", {
    method: "POST",
    body: JSON.stringify({
        "line": "the line"
    }),
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }
});

It works by using the first method $_POST.

  1. When using this code with PHPUnit:
$client->request('POST', "/test", [
    "line" => "the line"
], [], [ 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' ]);

It the second method that works (by using request->get).

Why is there a difference and what am I doing wrong?

3

Answers


  1. When you use $request->request->get("line") Symfony is expecting application/x-www-form-urlencoded as content type.

    So you need to handle your body as a json in your symfony controller:

    $line = json_decode($request->getContent(), true);
    

    Or in symfony >= 5.2

    $line = $request->toArray();
    

    You could also modify your ajax request to be treated as application/x-www-form-urlencoded.

    You may also use of the new symfony 6 attribute to map your payload to a new dto in your method parameters (symfony >= 6.3).

    Login or Signup to reply.
  2. In your JavaScript example, you send the POST data as a JSON body:

    • body is built by using body: JSON.stringify()
    • you specified the HTTP header 'Content-Type': 'application/json'

    It means the HTTP request body has been sent as a JSON string:

    {"line":"the line"}
    

    That’s why your 1st option works, but you can see that you need to decode the JSON before any other operation: json_decode($request->getContent(), true). PHP is not built to handle POST data in JSON format by default.

    The 2nd example is the standard one. By default, POST data is sent with a Content-Type HTTP header with the value application/x-www-form-urlencoded. The request body looks like a query string (like GET data):

    line=the%20line
    

    In this case, the $_POST superglobal is already configured to contain a line key, you don’t need to JSON decode anything. That’s why Symfony’s Request (as it uses the $_POST superglobal in its web runtime) can get your data using $request->request->get().


    If you want your JS code and PHPUnit code to work, you should either:

    • send the POST data in your JSON in the "default" format: x-www-form-urlencoded
    • encode the POST data from your PHPUnit test case in JSON (looks like you did not use the request() method properly)

    PS: don’t create/rewrite superglobals like $_POST, if not a bad practice, it’s at least very confusing. Never use such variables in Symfony anyways.

    Login or Signup to reply.
  3. What you are doing is wrong:

    Nothing crazy needs to be done, it’s just the the approaches to send data, and each approach requires a different way to handle the data on the PHP side.

    If you want to use the same approach for both scenarios, you could consider using a library like symfony/http-foundation to handle the JSON requests in a more standardized way.

    Here is the example on your code base to handle the request the PHPUnit test to send a raw JSON payload, similar to the JavaScript example, by using the json key in the request method.

    $client->request('POST', "/test", [], [], [
        'CONTENT_TYPE' => 'application/json',
        'ACCEPT' => 'application/json',
        'json' => ['line' => 'the line']
    ]);
    

    This would send a raw JSON payload; you could use the first method to handle it in your PHP code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search