Api: How to register exception handle to manage any exception other than the HTTP exception / Dingo related exceptions

Created on 24 Mar 2016  路  3Comments  路  Source: dingo/api

Hi,

Say, you want any API related errors to be handled by Dingo's Exception Handler. But other than that, you want to handle it your own, with your own message.

The current setup will output the error verbosely. For example, when the application faced an ErrorException, it will return this response:

{
  "error": {
    "message": "Trying to get property of non-object",
    "status_code": 500
  }
}

What I want is something like:

{
  "error": {
    "message": "Ooops. We have faced some problem. Our team has been notified. Fixing it now.",
    "status_code": 500
  }
}

I could register a handler for the ErrorException. But what I really would like to do is handle any exception that is not an instanceof HttpException.

I also realised that HttpException is extending the RuntimeException exception. That means, I cannot have my own RuntimeException handler, because that will also catch the HttpException raised by the app.

Do you guys have any solution for how I should approach the above problem? Or do you guys think that I have viewed the above problem incorrectly.

Thanks in advance for your help.

Most helpful comment

Nope, can be solved much easier...

app(Dingo\Api\Exception\Handler::class)->register(function (Exception $exception) {
    if (! $exception instanceof Symfony\Component\HttpKernel\Exception\HttpException) {
        throw new Symfony\Component\HttpKernel\Exception\HttpException(500, 'Woops, something went really wrong.');
    }
});

Or just return a custom response instead of throwing an HttpException.

Basically, catch all exceptions, then only if the exception is not an instance of the HttpException will something be thrown/returned. Otherwise just do nothing, it'll be handled by the generic handler then.

All 3 comments

I have a tried at solving the above problem. My solution so far is to override the Dingo Exception Handler:

<?php

namespace App\Exceptions;

use Dingo\Api\Exception\Handler;
use Exception;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;

class ApiExceptionHandler extends Handler
{
    public function handle(Exception $exception)
    {
        if ($exception instanceof HttpException) {
            return $this->genericResponse($exception);
        }

        return $this->handleRegisteredExceptions($exception);
    }

    protected function handleRegisteredExceptions(Exception $exception)
    {
        foreach ($this->handlers as $hint => $handler) {
            if (! $exception instanceof $hint) {
                continue;
            }

            if ($response = $handler($exception)) {
                if (! $response instanceof Response) {
                    $response = new Response($response, $this->getExceptionStatusCode($exception));
                }

                return $response;
            }
        }

        return $this->genericResponse(new Exception("Something went wrong from our side. Our team has been notified about this error, so that they can fix the problem soon. We are sorry for the inconvenience."), 500);
    }
}

So, any HttpException are handled by the original handler. Other than that, it is handled by the above Handler.

In order for the above Handler to work, I register it to override the api.exception binding. This is from my AppServiceProvider::register method

$this->app->singleton('api.exception', function($app) {
            return new ApiExceptionHandler($app['Illuminate\Contracts\Debug\ExceptionHandler'], config('api.errorFormat'), config('api.debug'));
        });

So far it's working fine.

But I would like to know the opinion from the community, do you think the above approach is good?

Nope, can be solved much easier...

app(Dingo\Api\Exception\Handler::class)->register(function (Exception $exception) {
    if (! $exception instanceof Symfony\Component\HttpKernel\Exception\HttpException) {
        throw new Symfony\Component\HttpKernel\Exception\HttpException(500, 'Woops, something went really wrong.');
    }
});

Or just return a custom response instead of throwing an HttpException.

Basically, catch all exceptions, then only if the exception is not an instance of the HttpException will something be thrown/returned. Otherwise just do nothing, it'll be handled by the generic handler then.

If you want to customize ur ignored list, you can do the following:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Exception;


class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //fix key was too long error
        Schema::defaultStringLength(191);

        //list of exceptions we don't want to be reported by dingo exception handler
        $dontReport = [
            \Illuminate\Auth\AuthenticationException::class,
            \Illuminate\Auth\Access\AuthorizationException::class,
            \Illuminate\Database\Eloquent\ModelNotFoundException::class,
            \Illuminate\Session\TokenMismatchException::class,
            \Illuminate\Validation\ValidationException::class,
            \Illuminate\Database\QueryException::class
        ];

        app('Dingo\Api\Exception\Handler')->register(function (Exception $exception) use ($dontReport) {
            if (in_array(get_class($exception), $dontReport)) {
                throw new HttpException(500, 'Woops, something went really wrong.');
            }
        });
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}


Was this page helpful?
0 / 5 - 0 ratings

Related issues

lloricode picture lloricode  路  3Comments

nghiepit picture nghiepit  路  4Comments

BartHuis picture BartHuis  路  3Comments

Grummfy picture Grummfy  路  4Comments

HTMHell picture HTMHell  路  4Comments