How to send Laravel error responses as JSON
Asked Answered
T

8

23

Im just move to laravel 5 and im receiving errors from laravel in HTML page. Something like this:

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

1/1
NotFoundHttpException in Application.php line 756:
Persona no existe
in Application.php line 756
at Application->abort('404', 'Person doesnt exists', array()) in helpers.php line 

When i work with laravel 4 all works fine, the errors are in json format, that way i could parse the error message and show a message to the user. An example of json error:

{"error":{
"type":"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",
"message":"Person doesnt exist",
"file":"C:\\xampp\\htdocs\\backend1\\bootstrap\\compiled.php",
"line":768}}

How can i achieve that in laravel 5.

Sorry for my bad english, thanks a lot.

Transnational answered 11/2, 2015 at 0:35 Comment(1)
Specific to the abort function, it is possible to provide abort (and related abort_if, abort_unless) with a custom Response object (in the place where you'd put the status code) if you need more control than the options provided by the abort function. For example, if you need a JSON response (and cannot set the accept header to application/json), you could do abort(new JsonResponse('A JSON string, could also be an array etc.', 403, ['Optional' => 'Headers'])Uella
V
26

I came here earlier searching for how to throw json exceptions anywhere in Laravel and the answer set me on the correct path. For anyone that finds this searching for a similar solution, here's how I implemented app-wide:

Add this code to the render method of app/Exceptions/Handler.php

if ($request->ajax() || $request->wantsJson()) {
    return new JsonResponse($e->getMessage(), 422);
}

Add this to the method to handle objects:

if ($request->ajax() || $request->wantsJson()) {

    $message = $e->getMessage();
    if (is_object($message)) { $message = $message->toArray(); }

    return new JsonResponse($message, 422);
}

And then use this generic bit of code anywhere you want:

throw new \Exception("Custom error message", 422);

And it will convert all errors thrown after an ajax request to Json exceptions ready to be used any which way you want :-)

Victuals answered 16/8, 2015 at 13:41 Comment(3)
if Laravel 5.1, the return should read "return response()->json($e->getMessage(), 422);"Woodcutter
This works. When the handling code is not there, Laravel returns an HTTP 500 error, regardless of the specific error thrown in the code. E.g. abort(403) would return a 500 error for ajax requests. Did you experience the same? This must be a bug?Selfdelusion
This saved my life. It's 2017 and I'm still using 5.1 so this is really useful for me. I passed 200 to the second argument when handling a FatalErrorException because I want the user to get an alert with a useful message rather than an obscure message like Internal Server Error. This lets the page render normally and gives the user useful feedback.Landel
I
12

Laravel 5.1

To keep my HTTP status code on unexpected exceptions, like 404, 500 403...

This is what I use (app/Exceptions/Handler.php):

 public function render($request, Exception $e)
{
    $error = $this->convertExceptionToResponse($e);
    $response = [];
    if($error->getStatusCode() == 500) {
        $response['error'] = $e->getMessage();
        if(Config::get('app.debug')) {
            $response['trace'] = $e->getTraceAsString();
            $response['code'] = $e->getCode();
        }
    }
    return response()->json($response, $error->getStatusCode());
}
Inspector answered 3/2, 2016 at 23:0 Comment(0)
L
6

Laravel 5 offers an Exception Handler in app/Exceptions/Handler.php. The render method can be used to render specific exceptions differently, i.e.

public function render($request, Exception $e)
{
    if ($e instanceof API\APIError)
        return \Response::json(['code' => '...', 'msg' => '...']);
    return parent::render($request, $e);
}

Personally, I use App\Exceptions\API\APIError as a general exception to throw when I want to return an API error. Instead, you could just check if the request is AJAX (if ($request->ajax())) but I think explicitly setting an API exception seems cleaner because you can extend the APIError class and add whatever functions you need.

Leucas answered 2/4, 2015 at 14:15 Comment(0)
N
6

Edit: Laravel 5.6 handles it very well without any change need, just be sure you are sending Accept header as application/json.


If you want to keep status code (it will be useful for front-end side to understand error type) I suggest to use this in your app/Exceptions/Handler.php:

public function render($request, Exception $exception)
{
    if ($request->ajax() || $request->wantsJson()) {

        // this part is from render function in Illuminate\Foundation\Exceptions\Handler.php
        // works well for json
        $exception = $this->prepareException($exception);

        if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
            return $exception->getResponse();
        } elseif ($exception instanceof \Illuminate\Auth\AuthenticationException) {
            return $this->unauthenticated($request, $exception);
        } elseif ($exception instanceof \Illuminate\Validation\ValidationException) {
            return $this->convertValidationExceptionToResponse($exception, $request);
        }

        // we prepare custom response for other situation such as modelnotfound
        $response = [];
        $response['error'] = $exception->getMessage();

        if(config('app.debug')) {
            $response['trace'] = $exception->getTrace();
            $response['code'] = $exception->getCode();
        }

        // we look for assigned status code if there isn't we assign 500
        $statusCode = method_exists($exception, 'getStatusCode') 
                        ? $exception->getStatusCode()
                        : 500;

        return response()->json($response, $statusCode);
    }

    return parent::render($request, $exception);
}
Naominaor answered 4/1, 2017 at 14:38 Comment(1)
This worked really well, returns a huge json with stack trace, but can easly see last error at the topNicol
S
4

On Laravel 5.5, you can use prepareJsonResponse method in app/Exceptions/Handler.php that will force response as JSON.

/**
 * Render an exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    return $this->prepareJsonResponse($request, $exception);
}
Smallpox answered 29/11, 2017 at 7:45 Comment(1)
Nice and simple! But, Is it secure to show all that message to the client side?Boardinghouse
C
0

Instead of

if ($request->ajax() || $request->wantsJson()) {...}

use

if ($request->expectsJson()) {...}

vendor\laravel\framework\src\Illuminate\Http\Concerns\InteractsWithContentTypes.php:42

public function expectsJson()
{
    return ($this->ajax() && ! $this->pjax()) || $this->wantsJson();
}
Czernowitz answered 11/10, 2017 at 12:24 Comment(1)
explain your answer so that OP and future readers will understand better.Eugenle
C
0

I updated my app/Exceptions/Handler.php to catch HTTP Exceptions that were not validation errors:

public function render($request, Exception $exception)
{
    // converts errors to JSON when required and when not a validation error
    if ($request->expectsJson() && method_exists($exception, 'getStatusCode')) {
        $message = $exception->getMessage();
        if (is_object($message)) {
            $message = $message->toArray();
        }

        return response()->json([
            'errors' => array_wrap($message)
        ], $exception->getStatusCode());
    }

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

By checking for the method getStatusCode(), you can tell if the exception can successfully be coerced to JSON.

Cornemuse answered 18/10, 2017 at 21:49 Comment(0)
A
0

If you want to get Exception errors in json format then open the Handler class at App\Exceptions\Handler and customize it. Here's an example for Unauthorized requests and Not found responses

public function render($request, Exception $exception)
{
    if ($exception instanceof AuthorizationException) {
        return response()->json(['error' => $exception->getMessage()], 403);
    }

    if ($exception instanceof ModelNotFoundException) {
        return response()->json(['error' => $exception->getMessage()], 404);
    }

    return parent::render($request, $exception);
}
Admiralty answered 5/9, 2019 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.