33

Is there a clean, easy way to throw php exceptions through a json response jquery/ajax call.

madphp
  • 1,716
  • 5
  • 31
  • 72
  • 2
    Why do you want to throw an exception? It would likely be better to return an error state that does not trigger an exception. – doublesharp Oct 02 '12 at 15:30
  • 2
    I mean I have try catch on the server side, and I want to put the error message into the json response. – madphp Oct 02 '12 at 15:33

5 Answers5

63

You could do something like this in PHP (assuming this gets called via AJAX):

<?php

try {
    if (some_bad_condition) {
        throw new Exception('Test error', 123);
    }
    echo json_encode(array(
        'result' => 'vanilla!',
    ));
} catch (Exception $e) {
    echo json_encode(array(
        'error' => array(
            'msg' => $e->getMessage(),
            'code' => $e->getCode(),
        ),
    ));
}

In JavaScript:

$.ajax({
    // ...
    success: function(data) {
        if (data.error) {
            // handle the error
            throw data.error.msg;
        }
        alert(data.result);
    }
});

You can also trigger the error: handler of $.ajax() by returning a 400 (for example) header:

header('HTTP/1.0 400 Bad error');

Or use Status: if you're on FastCGI. Note that the error: handler doesn't receive the error details; to accomplish that you have to override how $.ajax() works :)

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
8

Facebook do something in their PHP SDK where they throw an exception if a HTTP request failed for whatever reason. You could take this approach, and just return the error and exception details if an exception is thrown:

<?php

header('Content-Type: application/json');

try {
    // something; result successful
    echo json_encode(array(
        'results' => $results
    ));
}
catch (Exception $e) {
    echo json_encode(array(
        'error' => array(
            'code' => $e->getCode(),
            'message' => $e->getMessage()
        )
    ));
}

You can then just listen for the error key in your AJAX calls in JavaScript:

<script>
    $.getJSON('http://example.com/some_endpoint.php', function(response) {
        if (response.error) {
            // an error occurred
        }
        else {
            $.each(response.results, function(i, result) {
                // do something with each result
            });
        }
    });
</script>
Martin Bean
  • 38,379
  • 25
  • 128
  • 201
4

If all the errors should be treated in the same way (showing a dialog for example). You can do it this way:

PHP End:

public function throwJsonException($msg) {
    echo json_encode(array('error'=> true, 'msg' => $msg));
}

throwJsonException('login invalid!');

jQuery End:

$(document).ajaxSuccess(function(evt, request, settings){
    var data=request.responseText;
    if (data.length>0) {
        var resp=$.parseJSON(data);
        if (resp.error)
        {
            showDialog(resp.msg);
            return;
        }                   
    }    
});
MazarD
  • 2,759
  • 2
  • 22
  • 33
3

As a complement to the previous answers, instead of repeating the same code for json encoding in all your exceptions, you could set an exception handler to be used only in the needed scripts. For instance:

function ajaxExceptionHandler($e) {
    echo json_encode(array(
        'error' => array(
        'code' => $e->getCode(),
        'msg' => $e->getMessage())
    ));
}

Then, in your ajax handler,

set_exception_handler('ajaxExceptionHandler');
Pere
  • 1,068
  • 12
  • 20
1

I use this approach to convert Exceptions to JSON:

<?php

namespace MyApp\Utility;

use Throwable;

trait JsonExceptionTrait
{    
    public function jsonException(Throwable $exception)
    {
        return json_encode([
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTrace()
        ]);
    }
}

then, simply in my controllers:

<?php

namespace MyApp\Controller;

use MyApp\Utility\JsonExceptionTrait;

class UserController extends AbstractController
{
    use JsonExceptionTrait;

     /**
     * @param int $userId
     * @return JsonResponse
     */
    public function getAction(int $userId): JsonResponse
    {
        try {
            $user = $this->userService->getUser($userId);
            return new Response($user);
        } catch (EntityNotFoundException $exception) {
            return new Response(
                $this->jsonException($exception),
                JsonResponse::HTTP_NOT_FOUND
            );
        }
    }
OKE
  • 364
  • 1
  • 5
  • 13