Adds methods to your App\Exceptions\Handler to treat json responses.
It is most useful if you are building APIs!
- PHP >= 8.1
- Laravel Framework 10.x, 11.x, or 12.x
Using JsonAPI standard to responses!
Default error response:
{
"errors": [
{
"status": "404",
"code": "13",
"title": "model_not_found_exception",
"detail": "User not found",
"source": {
"pointer": "data/id"
}
}
]
}To Illuminate\Validation\ValidationException:
{
"errors": [
{
"status": "422",
"code": "1411",
"title": "Required validation failed on field name",
"detail": "The name field is required.",
"source": {
"pointer": "name"
}
},
{
"status": "422",
"code": "1421",
"title": "Email validation failed on field email",
"detail": "The email must be a valid email address.",
"source": {
"pointer": "email"
}
}
]
}Illuminate\Auth\Access\AuthorizationExceptionIlluminate\Auth\AuthenticationExceptionIlluminate\Database\Eloquent\ModelNotFoundExceptionIlluminate\Validation\ValidationExceptionLaravel\Passport\Exceptions\MissingScopeExceptionLeague\OAuth2\Server\Exception\OAuthServerExceptionSymfony\Component\HttpKernel\Exception\NotFoundHttpExceptionSymfony\Component\HttpKernel\Exception\BadRequestHttpException
Install the package
$ composer require sfelix-martins/json-exception-handlerThe package uses Laravel's auto-discovery feature, so the service provider will be automatically registered.
Publish the config to set your own exception codes
$ php artisan vendor:publish --provider="SMartins\Exceptions\JsonHandlerServiceProvider"Set your exception codes on config/json-exception-handler.php on codes array.
You can add more fields and codes to validation_fields array.
You can add too your models on lang packages to return the Not Found response translated correctly.
In resources/lang/vendor/exception/lang/$locale in exceptions file you can set on models array. Example:
'models' => [
'User' => 'Usuário',
'Article' => 'Artigo',
]Use the trait on your App\Exception\Handler and add method jsonResponse()
passing the $exception if $request expects a json response on render() method
use SMartins\Exceptions\JsonHandler;
use Throwable;
class Handler extends ExceptionHandler
{
use JsonHandler;
// ...
public function render($request, Throwable $e)
{
if ($request->expectsJson()) {
return $this->jsonResponse($e);
}
return parent::render($request, $e);
}
// ...class UserController extends Controller
{
// ...
public function store(Request $request)
{
// Validation
$request->validate($this->rules);
// or
$this->validate($request, $this->rules);
//and or
Validator::make($request->all(), $this->rules)->validate();
if (condition()) {
// Generate response with http code and message
abort(403, 'Action forbidden!');
}
if (anotherCondition()) {
// Generate response with message and code
throw new TokenMismatchException("Error Processing Request", 10);
}
}
public function show($id)
{
// If not found the default response is called
$user = User::findOrFail($id);
// Gate define on AuthServiceProvider
// Generate an AuthorizationException if fail
$this->authorize('users.view', $user->id);
}You can create your own handler for any Exception. E.g.:
- Create a Handler class that extends
AbstractHandler:
namespace App\Exceptions;
use GuzzleHttp\Exception\ClientException;
use SMartins\Exceptions\Handlers\AbstractHandler;
use SMartins\Exceptions\JsonApi\Error;
use SMartins\Exceptions\JsonApi\Source;
use SMartins\Exceptions\Response\ErrorHandledCollectionInterface;
use SMartins\Exceptions\Response\ErrorHandledInterface;
class GuzzleClientHandler extends AbstractHandler
{
public function __construct(ClientException $e)
{
parent::__construct($e);
}
public function handle(): ErrorHandledInterface|ErrorHandledCollectionInterface
{
return (new Error)->setStatus((string) $this->getStatusCode())
->setCode((string) $this->getCode())
->setSource((new Source())->setPointer($this->getDefaultPointer()))
->setTitle($this->getDefaultTitle())
->setDetail($this->exception->getMessage());
}
public function getCode(string $type = 'default'): int|string
{
// You can add a new type of code on `config/json-exception-handlers.php`
return config('json-exception-handler.codes.client.default');
}
}- For returning multiple errors:
namespace App\Exceptions;
use SMartins\Exceptions\JsonApi\Error;
use SMartins\Exceptions\JsonApi\Source;
use SMartins\Exceptions\JsonApi\ErrorCollection;
use SMartins\Exceptions\Handlers\AbstractHandler;
use SMartins\Exceptions\Response\ErrorHandledCollectionInterface;
use SMartins\Exceptions\Response\ErrorHandledInterface;
class MyCustomizedHandler extends AbstractHandler
{
public function __construct(MyCustomizedException $e)
{
parent::__construct($e);
}
public function handle(): ErrorHandledInterface|ErrorHandledCollectionInterface
{
$errors = (new ErrorCollection)->setStatusCode('400');
$exceptions = $this->exception->getExceptions();
foreach ($exceptions as $exception) {
$error = (new Error)->setStatus('422')
->setSource((new Source())->setPointer($this->getDefaultPointer()))
->setTitle($this->getDefaultTitle())
->setDetail($exception->getMessage());
$errors->push($error);
}
return $errors;
}
}- Now just register your customized handler on
App\Exception\Handlerfile on attributeexceptionHandlers. E.g:
namespace App\Exceptions;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use SMartins\Exceptions\JsonHandler;
use Throwable;
class Handler extends ExceptionHandler
{
use JsonHandler;
protected $exceptionHandlers = [
// Set on key the exception and on value the handler.
ClientException::class => GuzzleClientHandler::class,
];If you're upgrading from version 2.x, note the following changes:
- PHP 8.1+ is now required
- Laravel 10, 11, or 12 is required
- The
render()method signature now usesThrowableinstead ofException - All handler classes now use strict types and proper return type declarations
- Status codes are now always strings in the JSON response (as per JsonAPI spec)
