43

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?

10 Answers 10

59

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

2 Comments

I really liked the second thing you said. About the API, though I didn't understand it entirely. Should I create another class, where I extends Response class and call it instead of the "right" one? It's that it?
@DennisBraga that is right. It helps you keep a uniformed error response in your API.
42

You can pass an array to the returned JSON response:

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

2 Comments

Thanks! In laravel 5 it would be: return response()->json($returnData, 500)
could also be made more easier if you reduce the useage of the $returnData array
23

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

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

Comments

21

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 :)

1 Comment

You are the King! with only a tiny snippet i can handle ajax response. Cool. example: abort( 401, 'Not Authorized' );
10

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."
 }

1 Comment

Greate I was missed that in logout API working fine now. Thanks
9

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);
}

Comments

4

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'));
}

2 Comments

invalidJson() don't existe in \app\Exceptions\Handler.php on laravel 8.
\app\Exceptions\Handler.php extends Illuminate\Foundation\Exceptions\Handler, invalidJson() exists in parent class, you just need to override it to change its behavior
2
$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);

Comments

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

1 Comment

Remember to add use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1

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);
}

1 Comment

That answer looke really good, but sadly it doesn't work for me, Laravel still gives me ```status "error" message "An error occurred."```` as response :(

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.