Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HttpFoundation] Add Request::toArray() for JSON content #38224

Merged
merged 1 commit into from Oct 3, 2020

Conversation

Nyholm
Copy link
Member

@Nyholm Nyholm commented Sep 17, 2020

Q A
Branch? master
Bug fix? ni
New feature? yes
Deprecations? no
Tickets
License MIT
Doc PR

The past few months I've been working more and more with javascript and APIs. I've written controllers that parse json from the request body. I've found myself copying code between projects so I looked at the possibility to add this code to the Request class itself.

Usage

POST /add-user
Content-Type: application/json

{"name": "Tobias", "email": "tobias.nyholm@gmail.com"}
use Symfony\Component\HttpFoundation\Exception\JsonException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

class MyController
    // ...
    public function addUser(Request $request): JsonResponse
    {
        try {
            $data = $request->toArray();
        } catch (JsonException $e) {
            return new JsonResponse(['message'=>'Request does not contain valid json'], 415);
        }
        
        $command = new AddUser($data['email'] ?? '', $data['name'] ?? '');
        
        try {
            $this->commandBus->dispatch($command);
        } catch (ValidationFailedException $e) {
            return new JsonResponse(['message' => 'Unexpected JSON body'], 400);
        }
        
        return new JsonResponse(['message' => 'User successfully added']);
    }
}

I've searched but I have not found that this has been proposed before.. With is strange.. ¯\(ツ)

@ro0NL
Copy link
Contributor

ro0NL commented Sep 17, 2020

what about using an InputBag for return? That way we're effectively decoding the request body (payload) as done when using $request->request .. but lazy.

Which raises another question, can we provide decoded payload from $request->request in a lazy way? The door was opened with #37327, and then a bit closed with #37265 :)

@ro0NL
Copy link
Contributor

ro0NL commented Sep 17, 2020

e.g. InputBag::fromString holding the string itself, then lazy decode it.

if ($_POST) {
   $this->request = new InputBag($_POST);
} elseif($this->getContent()) {
  $this->request = InputBag::fromString($this->getContent()[, $contentType]);
}

@ro0NL
Copy link
Contributor

ro0NL commented Sep 17, 2020

really, we get HTTP 400 for free this way :)

@lyrixx
Copy link
Member

lyrixx commented Sep 17, 2020

I don't have so much to say about the implementation but I have the same use case. I added a trait in my application to solve this issue, but having something native / in core would be really nice.

@Nyholm
Copy link
Member Author

Nyholm commented Sep 18, 2020

Thank you for the reviews and feedback.

That way we're effectively decoding the request body (payload) as done when using $request->request .. but lazy.

@ro0NL Im not sure what you mean. How is it lazy and what is the benefit?

Note that there is technically a difference between $_POST and the request body. For once, a HTTP GET request can have a body that wont be parsed into the $_POST super global.

@Nyholm Nyholm changed the title [HttpFoundation] Add Request::getContentArray() [HttpFoundation] Add Request::toArray() for JSON content Sep 18, 2020
@ro0NL
Copy link
Contributor

ro0NL commented Sep 18, 2020

AFAIK the $_POST vars are obtained from the request content. So this is about providing consistent access, to a decoded request body, using $request->request->get('key').

The lazy part is about decoding JSON only when needed ;)

If $_POST is filled, i think we can assume it always supersedes.

@nicolas-grekas nicolas-grekas added this to the next milestone Sep 18, 2020
@yceruto
Copy link
Member

yceruto commented Sep 19, 2020

Tests failures seems related though https://ci.appveyor.com/project/fabpot/symfony/builds/35272847#L1155.

1) Symfony\Component\HttpFoundation\Tests\RequestTest::testToArrayNonJson
Failed asserting that exception message 'Syntax error' contains 'Could not decode request body.'.

Copy link
Contributor

@OskarStark OskarStark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR header still uses getContentArray() in the example code, can you please rework this to match toArray() ? Thanks

@Nyholm
Copy link
Member Author

Nyholm commented Sep 20, 2020

Oops, Sorry.

The code example in the PR header is updated.

@OskarStark
Copy link
Contributor

Thank you! 🙏

@nicolas-grekas
Copy link
Member

(rebase needed)

@Nyholm
Copy link
Member Author

Nyholm commented Oct 1, 2020

Thank you for the ping. The PR is rebased

Copy link
Member

@lyrixx lyrixx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

@fabpot
Copy link
Member

fabpot commented Oct 3, 2020

Thank you @Nyholm.

@fabpot fabpot merged commit f589ff4 into symfony:master Oct 3, 2020
@Nyholm Nyholm deleted the get-content-array branch October 3, 2020 07:58
@Nyholm
Copy link
Member Author

Nyholm commented Oct 3, 2020

Thank you for merging

wouterj added a commit to symfony/symfony-docs that referenced this pull request Oct 3, 2020
This PR was merged into the master branch.

Discussion
----------

Adding docs about Request::toArray()

This will fix #14323.

The related PR is found here: symfony/symfony#38224

Commits
-------

fd03b5e Adding docs about Request::toArray()
fabpot added a commit that referenced this pull request Oct 4, 2020
…Campbell)

This PR was merged into the 5.2-dev branch.

Discussion
----------

Remove array return type from Request::toArray()

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #38400
| License       | MIT
| Doc PR        | -

Laravel already extends Symfony's `Request` class and defines it's own `toArray` method. #38224 added a new `toArray` method to this class with a different signature to the one that is in Laravel, causing fatal errors (laravel/framework#34660). I think the best course of action here is to remove the return type for now, and only add it in Symfony 6. This will allow Symfony 6.0 and Laravel 11 to synchronize adding the return type.

Older versions of Laravel can't just change their signature to add an array return type to them, because that would be a breaking change for Laravel users extending Laravel's request class. I'm thinking, in particular, API packages and the like, or just straight up application code.

Commits
-------

8b291a4 Remove array return type from Request::toArray()
@nicolas-grekas nicolas-grekas modified the milestones: next, 5.2 Oct 5, 2020
@fabpot fabpot mentioned this pull request Oct 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants