Different Response(JSON and webpage) in API and Website for Laravel 404 and 500?

Expects JSON is about headers, i do not like that solution for API errors, you can access the API through a browser. My solution is most of the times to filter by the URL route, because it starts with "api/...", which can be done like so $request->is('api/*').

If you have your routes that are not prefixes with /api, then this will not work. Change the logic to fit with your own structure.

public function render($request, Exception $exception)
{
    if ($exception instanceof NotFoundHttpException) {
        if ($request->is('api/*')) {
            return response()->json(['error' => 'Not Found'], 404);
        }
        return response()->view('404', [], 404);
    }
    return parent::render($request, $exception);
}

Just wanted to add an alternative to the above answers, which all seem to work as well.

After having the same problem and digging deeper, I took a slightly different approach:

  • your exception handle calls parent::render(...). If you look into that function, it will render a json response if your request indicates that it wantsJson() [see hints how that works here]

  • now, to turn all responses (including exceptions) to json I used the Jsonify Middleware idea from here, but applied it to the api MiddlewareGroup, which is by default assigned to RouteServiceProvider::mapApiRoutes()

Here is one way to implement it (very similar to referenced answer from above):

  1. Create the file app/Http/Middleware/Jsonify.php

    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    
    class Jsonify
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if ( $request->is('api/*') ) {
                $request->headers->set('Accept', 'application/json');
            }
            return $next($request);
        }
    }
    
  2. Add the middleware reference to your $routeMiddleware table of your app/Http/Kernel.php file:

        protected $routeMiddleware = [
        ...
           'jsonify' => \App\Http\Middleware\Jsonify::class,
        ...
        ];
    
  3. In that same Kernel file, add the jsonify name to the api group:

    protected $middlewareGroups = [
    ...
      'api' => [
        'jsonify',
        'throttle:60,1',
        'bindings',            
      ],
    ...
    ];
    

Result is that the middleware gets loaded for any requests that fall into the 'api' group. If the request url begins with api/ (which is slightly redundant I think) then the header gets added by the Jsonify Middleware. This will tell the ExceptionHandler::render() that we want a json output.