cphalcon icon indicating copy to clipboard operation
cphalcon copied to clipboard

[BUG]: "Response was already sent" exception with $this->response->send(), in Phalcon 5.1.3

Open ewaldberkers opened this issue 2 years ago • 5 comments

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

ewaldberkers avatar Jan 07 '23 14:01 ewaldberkers

You just need to check if it was sent or not:

if ($this->response->isSent() !== true ) {
   $this->response->send();
}

sinbadxiii avatar Jan 09 '23 05:01 sinbadxiii

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'.

ewaldberkers avatar Jan 09 '23 06:01 ewaldberkers

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' => '']);
}

rayanlevert avatar Feb 27 '23 10:02 rayanlevert

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.)

ewaldberkers avatar Feb 27 '23 12:02 ewaldberkers

I think you can just return this->response.

rudiservo avatar Sep 19 '23 17:09 rudiservo