How to correctly handle REST/API errors?

tl;dr: What should I do (how to configure my “api” app) to get JSON-formatted response (as told in the guide) and 404 Not Found error code:

{
    "name": "Not Found Exception",
    "message": "The requested resource was not found.",
    "code": 0,
    "status": 404
}

Because right now, all I am getting is either empty response body with 404 Not Found code or my application crashes and responds with 500 Internal Server Error instead.


In my “api” application I have controllers for my models/endpoints only, so I don’t have /api/controllers/SiteController.php. Naturally, I don’t have actionError method as well (since I don’t have a controller at all). This causes that when calling a non-existing endpoint, instead of JSON-formatter response, I am getting a raw exception:

An Error occurred while handling another error:
yii\base\InvalidRouteException: Unable to resolve the request "site/error". in (...)\vendor\yiisoft\yii2\base\Module.php:561
Stack trace:
#0 (...)\vendor\yiisoft\yii2\web\ErrorHandler.php(109): yii\base\Module->runAction('site/error')
#1 (...)\vendor\yiisoft\yii2\base\ErrorHandler.php(152): yii\web\ErrorHandler->renderException(Object(yii\web\NotFoundHttpException))

Bad! :[

Trying to resolve it, I have created SiteController and added configuration inside it:

public function behaviors()
{
    return [
        'access' => [
            'class' => AccessControl::class,
            'rules' => [
                [
                    'actions' => ['error'],
                    'allow' => true,
                ],
            ],
        ],
    ];
}

public function actions()
{
    return [
        'error' => [
            'class' => \yii\web\ErrorAction::class,
        ],
    ];
}

This time this ends with exception that a view (required by \yii\web\ErrorAction is not found):

An Error occurred while handling another error:
yii\base\ViewNotFoundException: The view file does not exist: C:\XAMPP\htdocs\akademia-slaska\tcmed-platform-web\api\views\site\error.php in C:\XAMPP\htdocs\akademia-slaska\tcmed-platform-web\vendor\yiisoft\yii2\base\View.php:233

Also bad.

I’ve digged into guide to find:

When handling a RESTful API request, if there is an error in the user request or if something unexpected happens on the server, you may simply throw an exception to notify the user that something went wrong. If you can identify the cause of the error (e.g., the requested resource does not exist), you should consider throwing an exception along with a proper HTTP status code (e.g., yii\web\NotFoundHttpException represents a 404 status code).

So, I have removed actions() method and replaced it with actionError() method instead:

public function actionError()
{
    throw new NotFoundHttpException('Boo!');
}

Not much change to be honest, another nasty exception, this time:

An Error occurred while handling another error:
yii\web\NotFoundHttpException: Boo! in C:\XAMPP\htdocs\akademia-slaska\tcmed-platform-web\api\controllers\SiteController.php:46

Something (but what) told me to keep actionError() empty:

public function actionError()
{
    
}

Finally, I am getting a correct response (my remote client finally gets 404 Not Found instead of 500 Internal Server Error). But with empty body (my client says “No body returned for response”) and marks that it received 0 bytes response.

In none of my voyages I managed to get to the result “promised” by the guide, i.e. getting correct error handling with JSON-formated error data in the body:

HTTP/1.1 404 Not Found
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

{
    "name": "Not Found Exception",
    "message": "The requested resource was not found.",
    "code": 0,
    "status": 404
}

What am I missing or doing wrong?

And the most important – why do I need SiteController at all? Why doesn’t Yii handle non-existing endpoints gracefully, i.e. returning 404, instead of 500?

It’s a partial response but maybe it is related to:
https://www.yiiframework.com/doc/api/2.0/yii-web-application#$defaultRoute-detail

The default route of this application. Defaults to ‘site’.

Nah! :slight_smile: This will only change default route, error handling etc. to some other controller than the default. But I still will have to deal with my own controller.

The question is how to achieve errors in API containing exactly the same content and formatted in exactly the same way, as the guide describes. Cause out-of-the box REST errors are not as shown in guide.

This question is kind of off-topic.

The default implementation of REST/API error handling does support throwing errors as in given example (i.e. with code, status, message etc.). But these errors occurs (are handled) inside every existing API endpoint (i.e. if you want to get user of ID = 55 from Users endpoint).

I was not getting this, because I was getting let’s say "higher level off errors) because I was trying to call a non-existing API endpoint. I believe that in this case Yii handles errors differently (unable to resolve given route to the controller) and that’s why I was getting pure 404 result without any extra details.

Getting a non-existing resource from an existing REST endpoint is a business-level error that must be handled correctly (i.e. in frontend) during regular application life and during every request’s life-cycle. Trying to call to a non-existing API endpoint is on the other hand a developer-error level that must be handled by changing the code and updating the repository. This kind of errors must not be handled durig regular request’s life-cycle.

1 Like