api: Custom ExceptionHandler

Is it possible to overwrite the exception handler? If I would like to add custom syntax for the errors responses. Would be awesome if we could handle the entire exception handler outside of this package and use the default one that comes with the package out of the box.

Dingo\Api\Routing\Router::__construct() must be an instance of Dingo\Api\Exception\Handler, instance of App\Exceptions\Handler given,

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 56 (34 by maintainers)

Most upvoted comments

Finally! Sorry to post on closed issue, but I must share this solution somewhere, I lost so many hours on it… First of all create your own exception handler: app/Exceptions/ApiExceptionsHandler.php

<?php
namespace App\Exceptions;

use Exception;
use Dingo\Api\Exception\Handler as DingoHandler;

class ApiExceptionsHandler extends DingoHandler
{
    public function handle(Exception $exception)
    {
        return parent::handle($exception);
    }
}

Then create wrapper for Laravel Dingo service provider: app/Providers/DingoServiceProvider.php

<?php
namespace App\Providers;

use Dingo\Api\Provider\LaravelServiceProvider;
use App\Exceptions\ApiExceptionsHandler as ExceptionHandler;

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

Lastly replace original Dingo service provider to this wrapper: /config/app.com

...
'providers' => [
...
    //Dingo\Api\Provider\LaravelServiceProvider::class, // <- Remove this
    App\Providers\DingoServiceProvider::class,
...
];

That’s it. Hope it’ll help someone.

Hi guys

It’s actually very easy to override the dingo exception handler. Just make a new exception handler class in your app, which extends the dingo one, and do the following in your AppServiceProvider

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->registerExceptionHandler();
    }

    /**
     * Register the exception handler - extends the Dingo one
     *
     * @return void
     */
    protected function registerExceptionHandler()
    {
        $this->app->singleton('api.exception', function ($app) {
            return new ApiExceptionHandler($app['Illuminate\Contracts\Debug\ExceptionHandler'], Config('api.errorFormat'), Config('api.debug'));
        });
    }

In this case, the ApiExceptionHandler is my custom exception handler. And so in extending dingo’s one, you are free to override any methods you wish.

And the class you want to extend is Dingo\Api\Exception\Handler

@shanginn thanks a lot, it work great.

I want to map all thrown exceptions in this packages to my Laravel exception handler class App\Exceptions\Handler instead of using the exception handler in this package. Since I want custom syntax for my API responses.

App\Providers\DingoServiceProvider::class

In case you are looking for a more robust error handler where jwt is included. I have modified the ApiExceptionsHandler as shown in the code below.

`<?php

namespace App\Exceptions;

use Exception;
use Dingo\Api\Exception\Handler as DingoHandler;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;

class ApiExceptionsHandler extends DingoHandler
{
    /**
     * @param \Dingo\Api\Http\Request $request
     * @param Exception $exception
     * @return \Illuminate\Http\JsonResponse|mixed
     * @throws Exception
     */
    public function render($request, Exception $exception)
    {
        // This will replace our 404 response with a JSON response.
        if ($exception instanceof ModelNotFoundException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'Resource item not found.'
            ], 404);
        }

        if ($exception instanceof NotFoundHttpException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'Resource not found.'
            ], 404);
        }

        if ($exception instanceof MethodNotAllowedHttpException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'Method not allowed.'
            ], 405);
        }

        if($exception instanceof TokenInvalidException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'Token is invalid'
            ], 403);
        }

        if ($exception instanceof TokenExpiredException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'Token is expired'
            ], 403);
        }

        if ($exception instanceof JWTException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => 'something is wrong'
            ], 403);
        }

        if($exception instanceof UnauthorizedHttpException || $request->wantsJson()) {
            return response()->json([
                'status' => false,
                'error' => $exception->getMessage()
            ], $exception->getStatusCode());
        }

        return parent::render($request, $exception);
    }
}`

I hope someone finds this helpful. Cheers!!!

@shanginn after doing this, I’m getting 500 response and no response data 😦

@jasonlewis I am quite new to Laravel and I am trying to rebind api.exception and implement totally custom exception handler. I can’t get it work.

I have custom exception handler like this:

class Handler extends \Dingo\Api\Exception\Handler {
    function handle(Exception $exception) {
        dd("test");
        return parent::handle($exception);
    }
}

I have registered the handler in AppServiceProvider's register() method:

$this->app->bind('api.exception', 'App\Exceptions\Handler');

The handle() method never get’s called. What am I doing wrong?

Thanks!

Well… you could rebind the Dingo exception handler as well. But what I was suggesting is to type-hint the standard Exception class in the Dingo exception handler and then pass the exception on to your applications handler.

app('api.exception')->register(function (Exception $exception) {
    return app('App\Exceptions\Handler')->render($excepton);
});