Issues

ZF-7825: Zend_Rest_Controller postAction cannot access request data

Description

I am extending Zend_Rest_Controller, and in my postAction method trying to access variables in the post request. I am calling:

$this->view->username = $this->getRequest()->getParam('username');

The username value returned from getParam() is null.

I am making this exact same call in my getAction and it finds the value fine in the request. Is getParam() not the correct call for a post request?

Comments

Changed component and assignee.

What is the value of $this->getRequest()->getRawBody()?

What I'm getting at is: what kind of payload is being sent to the action?

I've used getParam() in my own code with all actions with success, so my suspicion is that the parameter is either not present, or is part of the raw body -- which you'll then need to parse.

Here is my modified postAction


public function postAction() {
        error_log('this->getRequest()->getRawBody(): ' . print_r($this->getRequest()->getRawBody(),true));

        $this->view->username = $this->getRequest()->getParam('username');
        $this->view->requestid = $this->getRequest()->getParam('requestid');
        //$this->_forward('index');
    }

Here is the Apache error log output:

==> /var/log/apache2/error.log <==
[Thu Sep 10 22:04:27 2009] [error] [client 127.0.1.1] this->getRequest()->getRawBody(): {"username":"ajcoon","password":"foobar","requestid":"4aa9be3b22fe98.65658205"}

So, the data is being passed in by the client....so then you're saying, I need to define parsing logic myself? I guess that makes sense...

Perhaps this could result in a feature request for a helper class to parse POST/PUT data based on content type?

So, yes, you've stumbled onto one of the pitfalls of the request object: it doesn't currently have a way to decode the raw request body based on the Content-Type passed in.

I've built up an action helper for myself that checks the Content-Type request header for the string "application/json" , and, if found, pulls the data from there, passes it to Zend_Json::decode(), and then passes that data back into the request object. It's trivial to write currently, but we do need a standard solution for this.

I'm closing the issue for now (as it's marked as a bug); when I get a proposal written for the new functionality, I'll link here to it.

Great, thanks!

That's a good point. I think there's some RESTful Zend_Controller enhancements we should wrap up into a proposal as a consequence of people using Zend_Rest.

  1. Helper(s) to digest request bodies based on popular Content-Type values - xml, json, http-form-encoded
  2. Enhance ContextSwitch helper to be sensitive to Accept request headers for determining view modes so the format doesn't have to be in the URI

Others?

I would add:

  1. Extending views to support the concept of one response, many formats, again based on the Accept header. What I've done in my REST controllers is this:
ll application/views/scripts/helloworld/
total 24
-r--r--r-- 1 root root  82 2009-09-10 08:41 get.json.phtml
-r--r--r-- 1 root root 279 2009-09-09 16:40 _get_response.php
-r--r--r-- 1 root root  62 2009-09-10 08:41 get.xml.phtml
-r--r--r-- 1 root root  83 2009-09-10 23:16 post.json.phtml
-r--r--r-- 1 root root 279 2009-09-10 16:53 _post_response.php
-r--r--r-- 1 root root  63 2009-09-10 23:16 post.xml.phtml

I've defined a response structure in the _$action_response.php file. I then include this in my "resolvable" views, and just output different formats. For example:

cat application/views/scripts/helloworld/_get_response.php


<?php
$doc = new DOMDocument();
$doc->formatOutput = true;

$root_element = $doc->appendChild($doc->createElement("response"));
$root_element->appendChild( new DOMElement("requestid", $this->requestid));
$root_element->appendChild( new DOMElement("hello",$this->username) );

?>

cat application/views/scripts/helloworld/get.json.phtml


<?php
require('_get_response.php');
print Zend_Json::fromXML($doc->saveXML());
?>

cat application/views/scripts/helloworld/get.xml.phtml


<?php
require('_get_response.php');
print $doc->saveXML();
?>

What I'm envisioning is that the developer only has to implement the _$action_response.php file. Then, depending on the Accept format (assuming there is an encoder that supports it), we provide the encoding auto-magically. In my case, this would mean that by enabling json and xml in my ContextSwitch:


        $contextSwitch = $this->_helper->getHelper('contextSwitch');
        $contextSwitch->setAutoJsonSerialization(false);
        $contextSwitch->addActionContext('get', array('xml','json'))->initContext();\

...I've auto-magically made "get.json.phtml" and "get.xml.phtml" available as views.

Does this make sense?

In the projects I've been working on, I look for the Content-Type and Accept header in the request; based on that, I will then add all the various REST actions to the appropriate context (currently, only json). So, in the end, I'm doing similarly to what you describe.

As for the formatting -- I leave that entirely to my individual views. HTML, JSON, and XML all have very different needs, so I simply pass in the values, and then serialize them as necessary. One technique I'm using is to have my service layer always return Zend_Paginator objects for lists -- that way I know that I have the same general type of object, and there are particular serialization formats automatically available. (I've also built a view helper that will setup my "Content-Range" header based on the Paginator object, which is tremendously useful.)

I think having either ContextSwitch or a new action helper setup the ContextSwitch mappings for REST controllers makes sense; developers may always selectively switch off actions they don't want responding in the given format. Additionally, as Luke suggests, some automated scaffolding for pulling in the POST/PUT data based on the Content-Type of the request would be useful.

Matthew,

Do you want to write up a Zend_Rest proposal that incorporates all these? Or should we just make task tickets for them and get started? Or I can write a proposal if we want.

-L

Kanten of vulkanisert Salé is betont http://www.nikeshoxsko.biz with a fargerik 90'erne stil geometriske Mønster, which tar the already existing vintage Appell of this http://www.nikeshoxsko.biz silhuetten løbet top.Stop at the endClothing å legge the recent http://www.nikeshoxsko.biz Hardland from adidas Originals to the sneaker lineup today.