'How can I make Laravel return a custom error for a JSON REST API

I'm developing some kind of RESTful API. When some error occurs, I throw an App::abort($code, $message) error.

The problem is: I want him to throw a json formed array with keys "code" and "message", each one containing the above mentioned data.

Array
(
    [code] => 401
    [message] => "Invalid User"
)

Does any one knows if it's possible, and if it is, how I do it?



Solution 1:[1]

go to your app/start/global.php.

This will convert all errors for 401 and 404 to a custom json error instead of the Whoops stacktrace. Add this:

App::error(function(Exception $exception, $code)
{
    Log::error($exception);

    $message = $exception->getMessage();

    // switch statements provided in case you need to add
    // additional logic for specific error code.
    switch ($code) {
        case 401:
            return Response::json(array(
                    'code'      =>  401,
                    'message'   =>  $message
                ), 401);
        case 404:
            $message            = (!$message ? $message = 'the requested resource was not found' : $message);
            return Response::json(array(
                    'code'      =>  404,
                    'message'   =>  $message
                ), 404);        
    }

});

This is one of many options to handle this errors.


Making an API it is best to create your own helper like Responser::error(400, 'damn') that extends the Response class.

Somewhat like:

public static function error($code = 400, $message = null)
{
    // check if $message is object and transforms it into an array
    if (is_object($message)) { $message = $message->toArray(); }

    switch ($code) {
        default:
            $code_message = 'error_occured';
            break;
    }

    $data = array(
            'code'      => $code,
            'message'   => $code_message,
            'data'      => $message
        );

    // return an error
    return Response::json($data, $code);
}

Solution 2:[2]

You can pass an array to the returned JSON response:

$returnData = array(
    'status' => 'error',
    'message' => 'An error occurred!'
);
return Response::json($returnData, 500);

Solution 3:[3]

Here is what I use (Laravel 5.2):

According to: https://laravel.com/docs/5.2/errors , we can specify a custom render function for errors in app\Exceptions\Handler.php. All I did was to change my render function to this:

    /**
     * Render an exception into an HTTP response.
     * Updated to return json for a request that wantsJson 
     * i.e: specifies 
     *      Accept: application/json
     * in its header
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        if ($request->ajax() || $request->wantsJson()) {
            return response()->json(
                          $this->getJsonMessage($e), 
                          $this->getExceptionHTTPStatusCode($e)
                        );
        }
        return parent::render($request, $e);
    }

    protected function getJsonMessage($e){
        // You may add in the code, but it's duplication
        return [
                  'status' => 'false',
                  'message' => $e->getMessage()
               ];
    }

    protected function getExceptionHTTPStatusCode($e){
        // Not all Exceptions have a http status code
        // We will give Error 500 if none found
        return method_exists($e, 'getStatusCode') ? 
                         $e->getStatusCode() : 500;
    }

After this, all you need do is make sure all your API requests specify the Accept: application/json header. Hope this helps :)

Solution 4:[4]

Heres what I used in 5.6 in order to return the same type of response as the built-in validate method:

response()->json(['errors' => ['email' => ['The email is invalid.']]], 422);

Solution 5:[5]

According to Ibrahim's answer, not every ajax request wants JSON, Responding the "status code" and the "status" is unnecessary since they both mean the same thing. More than that, there's no need to mention in the response "status" at all, since the response code "says" that. Something like that should work perfectly:

/**
 * Render an exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $e)
{
    if ($request->wantsJson())
        return response()->json(
            ['message' => $e->getMessage()],
            method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500);

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

Solution 6:[6]

Laravel 6:

You have to set Accept:application/json header in your API request from client-side and Laravel will automatically return a JSON format error.

 {     
    "message": "Unauthenticated."
 }

Solution 7:[7]

$response['message'] ="The given data was invalid";
$error['country_id'] = ["The country field is required"];
$error['state_id'] = ["The state field is required"];
$error['name'] = ["The name field is required"];
$response['error'] = $error;
return response()->json($response,422);

Solution 8:[8]

For Laravel 8

go to your \app\Exceptions\Handler.php and override invalidJson method like this:

// Add this line at the top of the class
use Illuminate\Validation\ValidationException;


/**
 * Convert a validation exception into a JSON response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Validation\ValidationException  $exception
 * @return \Illuminate\Http\JsonResponse
 */
protected function invalidJson($request, ValidationException $exception)
{
    // You can return json response with your custom form
    return response()->json([
        'success' => false,
        'data' => [
            'code' => $exception->status,
            'message' => $exception->getMessage(),
            'errors' => $exception->errors()
        ]
    ], $exception->status);
}

Response Sample:

{
  "success": false,
  "data": {
    "code": 422,
    "message": "The given data was invalid.",
    "errors": {
      "password": [
        "The password field is required."
      ]
    }
  }
}

The original method was:

/**
 * Convert a validation exception into a JSON response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Validation\ValidationException  $exception
 * @return \Illuminate\Http\JsonResponse
 */
protected function invalidJson($request, ValidationException $exception)
{
    return response()->json([
        'message' => $exception->getMessage(),
        'errors' => $exception->errors(),
    ], $exception->status);
}

Response Sample:

{
  "message": "The given data was invalid.",
  "errors": {
    "password": [
      "The password field is required."
    ]
  }
}

Note that unauthenticated response is in separate method, so you can override it as well

/**
 * Convert an authentication exception into a response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Auth\AuthenticationException  $exception
 * @return \Symfony\Component\HttpFoundation\Response
 */
protected function unauthenticated($request, AuthenticationException $exception)
{
    return $request->expectsJson()
        // Here you can change the form of the json response
        ? response()->json(['message' => $exception->getMessage()], 401) // <-
        : redirect()->guest($exception->redirectTo() ?? route('login'));
}

Solution 9:[9]

In Laravel5.6, I usually specify a custom render function for errors in app\Exceptions\Handler.php. All I did was to change my render function to this:

/**
 * Render an exception into an HTTP response.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Exception               $e
 *
 * @return Response
 */
public function render($request, Exception $e)
{
    if ($request->wantsJson() && !($e instanceof ValidationException)) {
        $response = [
            'message' => (string)$e->getMessage(),
            'status_code' => 400,
        ];

        if ($e instanceof HttpException) {
            $response['message'] = Response::$statusTexts[$e->getStatusCode()];
            $response['status_code'] = $e->getStatusCode();
        } else if ($e instanceof ModelNotFoundException) {
            $response['message'] = Response::$statusTexts[Response::HTTP_NOT_FOUND];
            $response['status_code'] = Response::HTTP_NOT_FOUND;
        }

        if ($this->isDebugMode()) {
            $response['debug'] = [
                'exception' => get_class($e),
                'trace' => $e->getTrace()
            ];
        }

        return response()->json([
            'status'      => 'failed',
            'status_code' => $response['status_code'],
            'massage'     => $response['message'],
        ], $response['status_code']);
    }

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

Solution 10:[10]

public function register() {

    $this->renderable(function (NotFoundHttpException $e, $request) {
        $returnData = array(
            'status' => 'error',
            'message' => 'Record not found'
        );
        return response()->json($returnData, "404");
    });
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Philip
Solution 3
Solution 4 kjdion84
Solution 5
Solution 6 francisco
Solution 7 HARIPRASATH PANNEERSELVAM
Solution 8
Solution 9 Sheng.Zh
Solution 10 Tonny Anthony