Home » Php » Clean way to throw php exception through jquery/ajax and json

Clean way to throw php exception through jquery/ajax and json

Posted by: admin April 23, 2020 Leave a comment

Questions:

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

How to&Answers:

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 🙂

Answer:

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>

Answer:

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

Answer:

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

Answer:

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