slimphp / Slim-Http

A set of PSR-7 object decorators providing useful convenience methods

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

getParsedBody() not working in slim v4

esetnik opened this issue · comments

In https://github.com/slimphp/Slim-Http/blob/master/src/ServerRequest.php#L165

        $parsedBody = $this->serverRequest->getParsedBody();
        if ($parsedBody !== null) {
            return $parsedBody;
        }

The $parsedBody is checked against null. When a POST request is received with Content-Type: application/json the php server will set $_POST to an empty array.

In
https://github.com/Nyholm/psr7-server/blob/master/src/ServerRequestCreator.php#L54
https://github.com/Nyholm/psr7-server/blob/master/src/ServerRequestCreator.php#L75

The serverRequest $parsedBody is initialized to the empty array and fails the check above so no parsed body data is returned from $request->getParsedBody();.

This probably requires adding a middleware to parse application/json bodies and replace the parsed body.

There's already extensive handling for json bodies in slim-http. See https://github.com/slimphp/Slim-Http/blob/master/tests/ServerRequestTest.php#L722. The issue is that it's not compatible with Nyholm\Psr7Server.

Ah yes, I see. The decorator is missing the handling for parsing JSON bodies.

Well not exactly. The issue is that Nyholm\Psr7Server does not return null for getParsedBody() when the Content-Type: application/json because it sets the parsedBody to $post here

In php, when Content-Type is application/json $_POST is [] not null.

@esetnik this is a tough problem. We could perhaps make a method which forces the body parsing no matter what the existing parsed body is?

My concern with removing the null check is do we want to interfere with the underlying implementations’s parsed body and completely override it?

Right. And I made a change which fixed the issue for me.

        $parsedBody = $this->serverRequest->getParsedBody();

        if (!empty($parsedBody)) {
            return $parsedBody;
        }

But it's weird because this test case is already passing and fails with my change, whereas my sample project doesn't work. So I need to understand why this test case is behaving differently from my project.

The problem with using empty() is that an empty array is still a valid parsed body in my opinion, that’s why this is a hard problem. What do you truly consider to be empty? For me, a null check is the only way to assert that.

The problem with using empty() is that an empty array is still a valid parsed body in my opinion, that’s why this is a hard problem. What do you truly consider to be empty? For me, a null check is the only way to assert that.

I agree, a null check is the appropriate way to ensure the body has not already been parsed by the implementation.

It looks like the real issue might be the requirement of slim v4 to use Nyholm/psr7-server which passes an empty array for parsedBody in all cases. The reason the test cases still pass is that they don't actually test the Nyholm/psr7-server implementation of ServerRequestCreator::fromGlobals().

Unfortunately it looks like it's also broken in zend/dictatoros https://github.com/zendframework/zend-diactoros/blob/master/src/ServerRequestFactory.php#L79

@esetnik using Nyholm/psr7-server isn’t a requirement for Slim 4, the framework is PSR-7 agnostic, theoretically you can use whichever implementation you want. You just have to find the one that fits your needs.

What we can do however is provide a workaround in Slim-Http so you can force body parsing using the logic in this repo if needed since we will never be able to accommodate all the different implementations’ quirks.

Yeah the thing is that there's currently no option that I found which works with slim v4 and slim-http. I've tried each of the compatible factories and they all pass empty array not null. So there's currently no way to use getParsedBody() from slim-http with slim v4 if you're using json content-type as far as I can tell. And I didn't find any integration testing between slim-http and slim which would have caught this issue. We might want to add that, since I think the use-case for using slim-http without slim will be rare, and compatibility should always be ensured.

I think part of the confusion is that the psr7 standard says that getParsedBody() should return null only in the absence of body content. So the other psr7 libraries are behaving correctly by returning an empty array since in the case of json the body content is not absent. Technically I think that slim-http should be conforming to this specification and checking for an empty array before it decides to do it's own parsing. If the underlying implementation returns null it means no body content at all, so slim-http should return null. If the underlying implementation returns a non-empty object or array, sim-http should return the underlying implementations result. And if the underlying implementation returns an empty array, slim-http should try to parse the body.

    /**
     * Retrieve any parameters provided in the request body.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, this method MUST
     * return the contents of $_POST.
     *
     * Otherwise, this method may return any results of deserializing
     * the request body content; as parsing returns structured content, the
     * potential types MUST be arrays or objects only. A null value indicates
     * the absence of body content.
     *
     * @return null|array|object The deserialized body parameters, if any.
     *     These will typically be an array or object.
     */
    public function getParsedBody();

I’m fine with using empty() instead of the null check. Would you like to raise a PR?

@l0gicgate I opened #100 for you to review. Thanks for the feedback.

The same issue affects GuzzleHttp's PSR-7 implementation. It appears numerous "correct" implementations of PSR-7 demonstrate this same tendency, and the PR in question is a quick and easy solution.