Cms: JSON error responses should have relevant HTTP status codes

Created on 22 Feb 2019  Â·  3Comments  Â·  Source: craftcms/cms

Throughout Craft's controllers, anywhere that returns JSON with errors, the response is a 200.

It really should be 4xx or 5xx, so it can be properly handled.

A good solution might be to add this to craft\web\Controller::asErrorJson, and use that more universally.

I'm thinking something like this might be ideal:

public function asErrorJson($data, int $statusCode = 500): YiiResponse
{
    $data = is_string($data) ? ['error' => $error] : $data;

    return $this->asJson($data)
        ->setStatusCode($statusCode);
}

Or perhaps something else to unify all JSON responses (success, error, errors, message, etc: https://github.com/craftcms/cms/issues/2495)

content administration 🤓 enhancement

Most helpful comment

You could hack it in by creating a module with this:

use yii\base\Event;
use yii\web\Response;

Event::on(Response::class, Response::EVENT_BEFORE_SEND, function(Event $e) {
    /** @var Response $response */
    $response = $e->sender;

    if (
        $response->format === Response::FORMAT_JSON &&
        !empty($response->data['error']) &&
        $response->getStatusCode() === 200 &&
        \Craft::$app->getRequest()->getIsSiteRequest()
    ) {
        $response->setStatusCode(400);
    }
});

All 3 comments

Agree this would be better. However it would be a breaking change for anyone creating these requests using jQuery Ajax methods, as the success method would no longer be called on non-200 responses. (An improvement to be sure but will have to wait until Craft 4.)

Thanks @brandonkelly.

In the meantime I wonder if I can duck-punch craft\web\Controller with an override of asJson that checks for error(s) in the params – and load it in config/app.php?

You could hack it in by creating a module with this:

use yii\base\Event;
use yii\web\Response;

Event::on(Response::class, Response::EVENT_BEFORE_SEND, function(Event $e) {
    /** @var Response $response */
    $response = $e->sender;

    if (
        $response->format === Response::FORMAT_JSON &&
        !empty($response->data['error']) &&
        $response->getStatusCode() === 200 &&
        \Craft::$app->getRequest()->getIsSiteRequest()
    ) {
        $response->setStatusCode(400);
    }
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rynpsc picture rynpsc  Â·  3Comments

leigeber picture leigeber  Â·  3Comments

michaelhue picture michaelhue  Â·  3Comments

angrybrad picture angrybrad  Â·  3Comments

bitboxfw picture bitboxfw  Â·  3Comments