10

I want to show the different response for API and website. In api response I want to show json response with 404 and 500 for type of exception mainly for routes.

If a user try to request a route and route not found I want to show a response in json response for API and webpage for website.

I know and try the code into app/Exceptions/Handler.php

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

https://laravel.com/docs/5.4/errors#http-exceptions

but failed can anybody help me how can I set different responses for error pages.

Prashant Barve
  • 4,105
  • 2
  • 33
  • 42

6 Answers6

18

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);
}
mrhn
  • 17,961
  • 4
  • 27
  • 46
4

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.

paratechX
  • 41
  • 1
  • 3
  • 1
    Sadly, this works most of the time, but for some cases it will not work: if you get a "not-authorized" from a policy or gate, then your middleware may not have been loaded yet. Without jsonify loaded, you will get non-json output unless you set your headers accordingly (Accept Application/Json). So you can use this answer, just beware of the shortfalls... – paratechX Dec 22 '18 at 03:36
  • If you're adding this to the `api` middleware group, there's no need to check for `/api` in the request URL. The routing service provider applies the `api` middleware group and the `/api` prefix to routes listed in `api.php`. – miken32 Dec 10 '20 at 19:02
1

No need to hustle again for Laravel upgrade. You just need to define this method in the routes/api.php

Route::fallback(function(){
    return response()->json(['message' => 'Not Found!'], 404);
});
miken32
  • 42,008
  • 16
  • 111
  • 154
joash
  • 2,205
  • 2
  • 26
  • 31
  • This works fine with GET method. but same url with othre methods (POST, PUT, ...) wrill return 405 MethodNotAlowed. I recommend to define custom fallback route in end of your api route by this way(https://stackoverflow.com/a/47537029/6807353) – Meow Kim Apr 09 '20 at 05:12
0

I'm using Laravel 5.5.28, and am adding this in app/Exceptions/Handler.php

public function render($request, Exception $exception)
{
    // Give detailed stacktrace error info if APP_DEBUG is true in the .env
    if ($request->wantsJson()) {
      // Return reasonable response if trying to, for instance, delete nonexistent resource id.
      if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
        return response()->json(['data' => 'Resource not found'], 404);
      }
      if ($_ENV['APP_DEBUG'] == 'false') {
        return response()->json(['error' => 'Unknown error'], 400);
      }
    }
    return parent::render($request, $exception);
}

This expects that your API calls will be having a header with key Accept and value application/json.

Then a nonexistent web route returns the expected

Sorry, the page you are looking for could not be found

and a nonexistent API resource returns a JSON 404 payload.

Found the info here.

You could combine this with the answer looking for the instance of NotFoundHttpException to catch the 500. I imagine, however, that the stack trace would be preferred.

mikeDOTexe
  • 487
  • 5
  • 14
0

finally found this for 9.x

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
/**
 * Register the exception handling callbacks for the application.
 *
 * @return void
 */
public function register()
{
    $this->renderable(function (NotFoundHttpException $e, $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.'
            ], 404);
        }
    });
}

source: https://laravel.com/docs/9.x/errors#rendering-exceptions

-1

try this.

public function render($request, Exception $exception)
    {
        if ($request->ajax()) {
            return \Response::json([
                'success' => false,
                'message' => $exception->getMessage(),
            ], $exception->getCode());
        } else {
            return parent::render($request, $exception);
        }
    }
Shailesh Ladumor
  • 7,052
  • 5
  • 42
  • 55