Slim: Slim v3 does not parse multipart/form-data from PUT requests

When sending a POST, I can fetch all the fields using $request->getParsedBody() and it works perfectly. With a similar PUT request, the body is null hence parsing fails.

This is a long story. Because it is so intertwined with HTTP and streams, I couldn’t easily write a failing testcase… 😢

$app->post('/test', function ($request, $response) {
    var_dump($request->getParsedBody());
});

$app->put('/test', function ($request, $response) {
    var_dump($request->getParsedBody());
});

Requests are sent using Google Chrome and with javascript:

var data = new FormData
data.append('foo', 'bar')

request('POST', '/test', data, function() {
  console.log('POST success')
})

request('PUT', '/test', data, function() {
  console.log('PUT success')
})

request (method, uri, data, callback) {
  var request = new XMLHttpRequest()
  request.open(method, uri, true)
  request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
      callback(request);
    }
  }
  request.send(data)
}

The POST does show an array ['foo' => 'bar'], the PUT is null. Checking php’s input (php://input) marks clearly the input is there (also, Chrome devtools show this as expected). As Slim\Http\Body is a slight wrapper around streams, I started digging in Slim\Http\Request::getParsedBody():

public function getParsedBody()
{
    echo "getParsedBody()\n";
    if ($this->bodyParsed) {
        echo "Has  already parsed body\n";
        return $this->bodyParsed;
    }

    if (!$this->body) {
        echo "Has no body\n";
        return;
    }

    $mediaType = $this->getMediaType();
    $body = (string)$this->getBody();

    if (isset($this->bodyParsers[$mediaType]) === true) {
        echo "Has parser\n";
        $parsed = $this->bodyParsers[$mediaType]($body);

        if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) {
            throw new RuntimeException('Request body media type parser return value must be an array, an object, or null');
        }
        $this->bodyParsed = $parsed;
    } else {
        echo "Has no parser\n";
    }

    echo "Ready to return body\n";
    return $this->bodyParsed;
}

Output:

// POST: iterating double and 2nd time it has the body parsed?
getParsedBody()
Has no parser
Ready to return body
getParsedBody()
Has  already parsed body

// PUT: no parser, single iteration
getParsedBody()
Has no parser
Ready to return body

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 3
  • Comments: 16 (7 by maintainers)

Most upvoted comments

I think that if someone needs multipart/form-data in a PUT request, then they can write their own media type parser and register it with the Request object.

@micheh kinda weird answer imho. Slim is a general purpose framework, it should not be opinionated in the case it does not allow PUT or DELETE requests with multipart/form-data bodies. If you want to support all common HTTP verbs and support all common body encodings, it should be part of Slim.

Either way, I have done like @akrabat said already, it’s now a custom parser in my own app. If anyone need this for their own use in the future, just ping me and I’ll explain.

application/x-www-form-urlencoded is no good if you want to send binary data…

It should be noted if you are using the _METHOD overloads that, this is not an issue iirc.