10

I want to return a JSON response instead of the default 404 error page when ModelNotFoundException occurs. To do this, I wrote the following code into app\Exceptions\Handler.php :

public function render($request, Exception $exception)
{
    if ($exception instanceof ModelNotFoundException) {
        return response()->json([
            'error' => 'Resource not found'
        ], 404);
    }

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

However it doesn't work. When the ModelNotFoundException occurs, Laravel just shows a blank page. I find out that even declaring an empty render function in Handler.php makes Laravel display a blank page on ModelNotFoundException.

How can I fix this so it can return JSON/execute the logic inside the overriden render function?

Dilip Hirapara
  • 14,810
  • 3
  • 27
  • 49

4 Answers4

21

In Laravel 8x, You need to Rendering Exceptions in register() method

use App\Exceptions\CustomException;

/**
 * Register the exception handling callbacks for the application.
 *
 * @return void
 */
public function register()
{
    $this->renderable(function (CustomException $e, $request) {
        return response()->view('errors.custom', [], 500);
    });
}

For ModelNotFoundException you can do it as below.

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

public function register()
{
    $this->renderable(function (NotFoundHttpException $e, $request) {
        return response()->json(...);
    });
}

By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering Closure for exceptions of a given type. You may accomplish this via the renderable method of your exception handler. Laravel will deduce what type of exception the Closure renders by examining the type-hint of the Closure:

More info about the error exception

Dilip Hirapara
  • 14,810
  • 3
  • 27
  • 49
  • 1
    Glad to help you :) – Dilip Hirapara Oct 07 '20 at 14:35
  • 3
    Why do you have to use `NotFoundHttpException` exception in the handler for a `ModelNotFoundException`? I have an issue where the `NotFoundHttpException` is also thrown for 404 errors but I want to throw different errors for these exceptions – Lukerayner Apr 13 '21 at 09:18
7

This code doesn't work for me (in Laravel 8.74.0):

$this->renderable(function (ModelNotFoundException$e, $request) {
    return response()->json(...);
});

Don't know why, but ModelNotFoundException is directly forwarded to NotFoundHttpException (which is a part of Symfony Component) that used by Laravel and will ultimately triggers a 404 HTTP response. My workaround is checking the getPrevious() method of the exception:

$this->renderable(function (NotFoundHttpException $e, $request) {
  if ($request->is('api/*')) {
    if ($e->getPrevious() instanceof ModelNotFoundException) {
        return response()->json([
            'status' => 204,
            'message' => 'Data not found'
        ], 200);
    }
    return response()->json([
        'status' => 404,
        'message' => 'Target not found'
    ], 404);
  }
});

And then we will know that this exception come from ModelNotFoundException and return a different response with NotFoundHttpException.

Edit

This is why ModelNotFoundException thrown as NotFoundHttpException

TheArKa
  • 350
  • 2
  • 5
  • 12
2

This one is my Handler file:

use Throwable;

   public function render($request, Throwable $exception)
    {
 if( $request->is('api/*')){
   if ($exception instanceof ModelNotFoundException) {
                $model = strtolower(class_basename($exception->getModel()));
              
 return response()->json([
            'error' => 'Model not found'
        ], 404);
            }
  if ($exception instanceof NotFoundHttpException) {
 return response()->json([
            'error' => 'Resource not found'
        ], 404);
                
            }
}
}

This one is only for all request in API route. If you want to catch all request, so remove the first if.

Hamid Naghipour
  • 3,465
  • 2
  • 26
  • 55
  • This didn't work. I wanted it for my api path too so I left the first if but unfortunately its the same blank page. –  Oct 07 '20 at 14:30
0

Please note that by default Laravel emits a JSON representation of an exception ONLY when you send a request with the header parameter Accept: application/json! For all other requests, Laravel sends normal HTML rendered output.

b166er
  • 319
  • 4
  • 11