cphalcon
cphalcon copied to clipboard
[BUG]: "Response was already sent" exception with $this->response->send(), in Phalcon 5.1.3
While adapting my websites running Phalcon 3.4 to run with Phalcon 5, I noticed that AJAX doesn't seem to work. Webpages with Volt views are fine.
Bug description
When using $this->response->send()
in controllers (needed with $this->response->setJsonContent()
, for example), there is an exception "Response was already sent" (Phalcon\Http\Response\Exception) from $response->send()
in public/index.php, and output is not emitted.
To reproduce
TestController.php:
class TestController extends Controller
{
public function jsonAction()
{
$this->view->disable();
$this->response->setJsonContent(['test' => 'TEST']);
$this->sendResponse();
}
public function txtAction()
{
$this->view->disable();
$this->response->setContent('This is a test.');
$this->response->setContentType('text/plain');
$this->sendResponse();
}
/**
* Sending a response in different ways, set $strategy variable to try them.
*/
protected function sendResponse()
{
$strategy = 0; // [0, 1, 2, 3]
switch ($strategy) {
case 0:
// Should work, according to docs, but doesn't
$this->response->send();
break;
case 1:
// Works, but undesirable
$this->response->send();
exit;
case 2:
// Works, but undesirable
echo $this->response->getContent();
break;
case 3:
// Not sending a response doesn't work
break;
default:
throw new \LogicException("Undefined strategy: '$strategy'");
}
}
}
public/index.php:
$response = $application->handle($_GET['_url']);
$response->send(); // Phalcon\Http\Response\Exception: Response was already sent
Expected behavior Should emit JSON or plain text.
Details
- Phalcon version: 5.1.3
- PHP Version: 8.1.2
- Operating System: Ubuntu 22.04 in WSL
- Installation type: installing via PECL
- Server: Apache
You just need to check if it was sent or not:
if ($this->response->isSent() !== true ) {
$this->response->send();
}
No, that doesn't work. It only prevents the exception (that is, if done in the public/index.php), but the content is still not sent. If done in the controller, there's still the exception 'Response was already sent'.
I'm pretty sure you have to return the response from the actions (return $this->response
).
So instead of calling ->send()
or echo ->getContent()
, return simply the response and you should be good.
For example I got this on index.php
$oApplication->handle($_GET['_url'] ?? '/')->send();
And on my controller
public function indexAction(): ResponseInterface
{
return $this->response->setStatusCode(200)
->setJsonContent(['code' => 'OK', 'error' => '']);
}
Thanks for elucidating, levertr. I didn't try your solution, but it makes sense.
What I ended up doing, after checking out the Zephir code for the Application object, was returning false in the controller action. There's this comment in the code "Returning false from an action cancels the view".
public function jsonAction()
{
$this->response->setJsonContent(['test' => 'TEST']);
return false;
}
That seemed to solve the problem. (I couldn't find this possibility in the docs.)
I think you can just return this->response.