RESTful PUT not passing getBodyParams()

(I have also posted this question on Stack Overflow.)

I replaced the standard REST actionUpdate with one that only allows updates to the user password and returns the boolean result of save() rather than the $user object:





class UserController extends ActiveController

{

    // ...

    public function actions()

    {

        $actions = parent::actions();

        unset($actions['update']);

        return $actions;

    }


    // ...

    public function actionUpdate($id)

    {

        if (! Yii::$app->request->isPut) {

            throw new MethodNotAllowedHttpException('Please use PUT');

        }


        /** @var User $user */

        $user = User::findIdentity($id);


        if (Yii::$app->request->post('password') !== null) {

            $user->setPassword(Yii::$app->request->post('password'));

        }


        return $user->save();

    }


    // ...

}




Testing with Postman and Codeception, [Response] = true and [Status] = 200. Both expected. However, the update does not take.

The [Request] =




PUT http://localhost:8888/api/v1/users/1 {"password":"another password"}



…which is correct. When I print_r the Yii::$app->request->post() and Yii::$app->request->bodyParams in actionUpdate, both return an empty array. However, when I print_r the $_GET, both the id and password are returned. Model rules lists ‘password’ as safe.

Any ideas?

Mahalo, Joe

Are you sure you’ve sent the request to the same action, that you’ve been trying to debug? It’s a bit weird question BUT recently I’ve developed an API based on the Yii2 generic actions for REST and didn’t face any problem with that.

The most strange point is, that you are able to retrive parameters sent as POST as GET. Check twice if you have everything done correctly, it looks like as a mistake you’ve made somewhere at your app.

I sent trace messages to the log, and it identifies the action that is being called (updateAction). Also, I am doing the print_r from that action and the only request is the one I send in Postman. So I am pretty sure the answer to this question is yes.

I have been trying to figure this out for days, so you might say I have checked…and much more than twice. Was hoping that someone could give me something specific to look for.

Oh, and I also tried to go back to the generic actionUpdate(). It also returns status ok, and also does not pass the data in the bodyParams(). So I don’t believe the problem is that I am using a custom action.

Mahalo,

Joe

Could you put here your trace log for the request?

Here it is:

2015-08-01 21:21:39 [::1][2][-][trace][yii\base\Module::getModule] Loading module: debug

2015-08-01 21:21:39 [::1][2][-][trace][yii\base\Application::bootstrap] Bootstrap with yii\debug\Module::bootstrap()

2015-08-01 21:21:39 [::1][2][-][trace][yii\web\UrlRule::parseRequest] Request parsed with URL rule: v1/users/<id:\d[\d,]*>

2015-08-01 21:21:39 [::1][2][-][trace][yii\rest\UrlRule::parseRequest] Request parsed with URL rule: v1/users/<id:\d[\d,]*>

2015-08-01 21:21:39 [::1][2][-][trace][yii\web\Application::handleRequest] Route requested: ‘v1/user/update’

2015-08-01 21:21:39 [::1][2][-][trace][yii\base\Module::getModule] Loading module: v1

2015-08-01 21:21:39 [::1][2][-][trace][yii\base\Controller::runAction] Route to run: v1/user/update

2015-08-01 21:21:41 [::1][2][-][trace][yii\base\Action::runWithParams] Running action: yii\rest\UpdateAction::run()

2015-08-01 21:21:39 [::1][2][-][info][application] $_GET = [

'id' =&gt; '2'


'password' =&gt; 'anotherpassword'

]

$_SERVER = [

'REDIRECT_STATUS' =&gt; '200'


'HTTP_HOST' =&gt; 'localhost:8888'


'HTTP_CONNECTION' =&gt; 'keep-alive'


'CONTENT_LENGTH' =&gt; '0'


'HTTP_USER_AGENT' =&gt; 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36'


'HTTP_CACHE_CONTROL' =&gt; 'no-cache'


'HTTP_ORIGIN' =&gt; 'chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop'


'HTTP_POSTMAN_TOKEN' =&gt; '7f5c02a6-aa5e-e91e-8dc2-357e9724d82f'


'HTTP_ACCEPT' =&gt; '*/*'


'HTTP_DNT' =&gt; '1'


'HTTP_ACCEPT_ENCODING' =&gt; 'gzip, deflate, sdch'


'HTTP_ACCEPT_LANGUAGE' =&gt; 'en-US,en;q=0.8'


'PATH' =&gt; '/usr/bin:/bin:/usr/sbin:/sbin'


'SERVER_SIGNATURE' =&gt; ''


'SERVER_SOFTWARE' =&gt; 'Apache/2.2.29 (Unix) mod_fastcgi/2.4.6 mod_wsgi/3.4 Python/2.7.8 PHP/5.6.2 mod_ssl/2.2.29 OpenSSL/0.9.8zf DAV/2 mod_perl/2.0.8 Perl/v5.20.0'


'SERVER_NAME' =&gt; 'localhost'


'SERVER_ADDR' =&gt; '::1'


'SERVER_PORT' =&gt; '8888'


'REMOTE_ADDR' =&gt; '::1'


'DOCUMENT_ROOT' =&gt; '/Users/jmdemoor/Sites/dbos/web'


'SERVER_ADMIN' =&gt; 'you@example.com'


'SCRIPT_FILENAME' =&gt; '/Users/jmdemoor/Sites/dbos/web/index.php'


'REMOTE_PORT' =&gt; '58830'


'REDIRECT_QUERY_STRING' =&gt; 'password=anotherpassword'


'REDIRECT_URL' =&gt; '/v1/users/2'


'GATEWAY_INTERFACE' =&gt; 'CGI/1.1'


'SERVER_PROTOCOL' =&gt; 'HTTP/1.1'


'REQUEST_METHOD' =&gt; 'PUT'


'QUERY_STRING' =&gt; 'password=anotherpassword'


'REQUEST_URI' =&gt; '/v1/users/2?password=anotherpassword'


'SCRIPT_NAME' =&gt; '/index.php'


'PHP_SELF' =&gt; '/index.php'


'PHP_AUTH_USER' =&gt; 'jmdemoor'


'PHP_AUTH_PW' =&gt; '########'


'REQUEST_TIME_FLOAT' =&gt; 1438500099.49


'REQUEST_TIME' =&gt; 1438500099


'argv' =&gt; [


    0 =&gt; 'password=anotherpassword'


]


'argc' =&gt; 1

]

It looks like the app does a redirect after the first request. I am pretty sure of that basing on $_SERVER[‘redirect_url’] which is set to some value in case above.

I am sorry, but I don’t follow you. I understand that the $_SERVER[‘REDIRECT_URL’] variable is set by Apache. However, $_SERVER[‘REQUEST_METHOD’] is PUT. Are you saying that there is an HTTP GET request happening after the put but before the routing to actionUpdate takes place? How can that happen?

Mahalo,

Joe

I try to give you just an idea. You can catch the GET parameters even IF you do a post/put request, it is not s problem.

Here is what finally worked for me. In both cases, it had to do with how the HTTP PUT was submitted.

In Postman, I was passing the parameter as part of the URL. Actually, the parameters should be sent as part of the request body.

In Codeception, I got my clue here. So I had to


$I->haveHttpHeader('Content-Type','application/json');

before $I->sendPUT(…)

Hope this helps the next person.