8

I am trying to create a RESTful API using laravel, I'm trying to fetch a resource with an invalid ID, and the result is 404 since it is not found, but my problem is the response is not in JSON format, but a View 404 (by default) with HTML. Is there any way to convert the response into JSON? For this situation, I use Homestead.

I try to include a fallback route, but it does not seem to fit this case.

Route::fallback(function () {
    return response()->json(['message' => 'Not Found.'], 404);
});

I try to modify the Handler (App\Exceptions), but nothing change.

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        if ($request->ajax()) {
            return response()->toJson([
                'message' => 'Not Found.',
            ], 404);
        }
    }

    return parent::render($request, $e);
}
3
  • Does $e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException work? Commented May 31, 2019 at 20:07
  • You'll need to send the correct content-type: 'content-type':'application/json' Commented May 31, 2019 at 20:15
  • About ModelNotFoundException - No. it seems that it passed direct. I can not understand. When i use the GET method, to catch all results, it's, OK (Json response), but, in one case (One particular ID e.g), all results is a HTML response Commented May 31, 2019 at 20:57

7 Answers 7

8

For Laravel 9

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);
        }
    });
}
Sign up to request clarification or add additional context in comments.

3 Comments

in App/Exceptions/Handler.php
but If we wants to check instance of ModelNotFoundException than how can do it? & how we get model name & id if we needed for ModelNotFoundExcepton in laravel 9
for Haanlde ModelNotFoundException in laravel 9, we can do it as like this stackoverflow.com/a/75492554/14344959
3

if your project is only a RESTful API and no views, you could add a new middleware which add ['accept' => 'application/json'] header to all request. this will ensure that all response will return a json, instead of the views

<?php

namespace App\Http\Middleware;

use Closure;

class AddAjaxHeader
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $request->headers->add(['accept' => 'application/json']);
        return $next($request);
    }
}

and add it into Kernel.php

2 Comments

In this case, i dont use only API. This project have a frontend to. But i'll try to use your exmple, and give you some feedback. thanks
I added this to my api middleware group and it works well for those routes, which is helpful for many cases (e.g. when returning 403 from a policy auth failure in a controller.) Obviously it won't work for the case where a completely wrong URI is used!
3

In Laravel 9, as per Official Documentation 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.

so, we need to checking Previous Exception using $e->getPrevious() just check previous exception is instanceof ModelNotFoundException or not

see below my code

// app/Exceptions/Handler.php file

$this->renderable(function (NotFoundHttpException $e, $request) {
    if ($request->is('api/*')) {
        if ($e->getPrevious() instanceof ModelNotFoundException) {
            /** @var ModelNotFoundException $modelNotFound */
            $modelNotFound = $e->getPrevious();
            if($modelNotFound->getModel() === Product::class) {
                return response()->json([
                    'message' => 'Product not found.'
                ], 404);
            }
        }

        return response()->json([
            'message' => 'not found.'
        ], 404);
    }
});

additionally, In API Response, if you are getting view as a response instead of JSON during other HTTP responses like HTTP 422 (validation error), 500 Internal server error. because $request->wantsJson() uses the Accept header sent by the client to determine if it wants a JSON response. So you need to set 'Accept' header value as "application/json" in request. see Validation Error Response

Method 1 :- set accept value as a application/json to every incoming api requests using middleware

JsonResponseApiMiddleware middleware created for set accept header to every incoming api requests

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class JsonResponseApiMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        // $acceptHeader = $request->header('Accept');
        
        // set 'accept' header value as a json to all request.
        $request->headers->set('accept', 'application/json', true);

        return $next($request);
    }
}


set JsonResponseApiMiddleware middleware class to api middlewareGroups. for this open Kernel.php file at App\Http directory

// App/Http/Kernel.php file

    protected $middlewareGroups = [
        // ...

        'api' => [
            // ... other middleware group
            \App\Http\Middleware\JsonResponseApiMiddleware::class,
        ]
    ]

Method 2 :- set accept value as a application/json api requests only when any exception occurs, for this open Handler.php file at App\Exceptions directory. credit about this method 2

// App/Exceptions/Handler.php file

$this->renderable(function (Throwable $e, $request) {
    if($request->is('api/*') || $request->wantsJson()) {
        // set Accept request header to application/json
        $request->headers->set('Accept', 'application/json');
    }
});

Comments

2

You'll need to send the correct Accept header in your request: 'Accept':'application/json'.

Then Illuminate\Foundation\Exceptions\Handler will care of the formatting in the render method in your response:

return $request->expectsJson()
                    ? $this->prepareJsonResponse($request, $e)
                    : $this->prepareResponse($request, $e);

3 Comments

return response()->json() already sets the correct headers.
I'm talking about the request not the response. Appreciate the downvote though.
expectsJson checks for Accept: application/json though, not Content-Type. Should make sure to pass that header.
0

go to .env file and make app_debug = false then when you send the request make it to accept json on postman or other http client.

then on your code make like this

function store($id){
  $user = User::find($id);
if(!$user){
    abort(code:404, message:'user not found)
}
   return $user;
}

Comments

-1
     /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Throwable  $exception
     * @return \Symfony\Component\HttpFoundation\Response
     *
     * @throws \Throwable
     */
    public function render($request, Throwable $exception)
    {
        switch (class_basename($exception)) {
            case 'NotFoundHttpException':
            case 'ModelNotFoundException':
                $exception = new NotFoundHttpException('Not found');
                break;
        }

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

Comments

-1

You need to set APP_DEBUG in you .env file to false.

or, if you use a phpunit, like follows

config()->set('app.debug', false);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.