mirror of
https://github.com/flarum/framework.git
synced 2025-02-01 01:29:29 +08:00
Merge pull request #606 from flarum/error-handling
Use exception handlers instead of JsonApiSerializableInterface
This commit is contained in:
commit
640c6199ef
|
@ -18,7 +18,9 @@ use Flarum\Event\ConfigureNotificationTypes;
|
|||
use Flarum\Http\GenerateRouteHandlerTrait;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\ErrorHandler;
|
||||
use Tobscure\JsonApi\Exception\Handler\FallbackExceptionHandler;
|
||||
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
|
||||
|
||||
class ApiServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
|
@ -36,6 +38,21 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||
$this->app->singleton('flarum.api.routes', function () {
|
||||
return $this->getRoutes();
|
||||
});
|
||||
|
||||
$this->app->singleton(ErrorHandler::class, function () {
|
||||
$handler = new ErrorHandler;
|
||||
|
||||
$handler->registerHandler(new Handler\FloodingExceptionHandler);
|
||||
$handler->registerHandler(new Handler\IlluminateValidationExceptionHandler);
|
||||
$handler->registerHandler(new Handler\InvalidConfirmationTokenExceptionHandler);
|
||||
$handler->registerHandler(new Handler\ModelNotFoundExceptionHandler);
|
||||
$handler->registerHandler(new Handler\PermissionDeniedExceptionHandler);
|
||||
$handler->registerHandler(new Handler\ValidationExceptionHandler);
|
||||
$handler->registerHandler(new InvalidParameterExceptionHandler);
|
||||
$handler->registerHandler(new FallbackExceptionHandler($this->app->inDebugMode()));
|
||||
|
||||
return $handler;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,11 +25,18 @@ class Client
|
|||
protected $container;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @var ErrorHandler
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param ErrorHandler $errorHandler
|
||||
*/
|
||||
public function __construct(Container $container, ErrorHandler $errorHandler)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,11 +62,9 @@ class Client
|
|||
}
|
||||
|
||||
try {
|
||||
$response = $controller->handle($request);
|
||||
return $controller->handle($request);
|
||||
} catch (Exception $e) {
|
||||
$response = $this->container->make('Flarum\Api\Middleware\HandleErrors')->handle($e);
|
||||
return $this->errorHandler->handle($e);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
45
framework/core/src/Api/ErrorHandler.php
Normal file
45
framework/core/src/Api/ErrorHandler.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api;
|
||||
|
||||
use Exception;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler;
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
/**
|
||||
* @var JsonApiErrorHandler
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* @param JsonApiErrorHandler $errorHandler
|
||||
*/
|
||||
public function __construct(JsonApiErrorHandler $errorHandler)
|
||||
{
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $e
|
||||
* @return JsonApiResponse
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$response = $this->errorHandler->handle($e);
|
||||
|
||||
$document = new Document;
|
||||
$document->setErrors($response->getErrors());
|
||||
|
||||
return new JsonApiResponse($document, $response->getStatus());
|
||||
}
|
||||
}
|
38
framework/core/src/Api/Handler/FloodingExceptionHandler.php
Normal file
38
framework/core/src/Api/Handler/FloodingExceptionHandler.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Core\Exception\FloodingException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class FloodingExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof FloodingException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 429;
|
||||
$error = [];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Validation\ValidationException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class IlluminateValidationExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof ValidationException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 422;
|
||||
|
||||
$errors = $e->errors()->toArray();
|
||||
$errors = array_map(function ($field, $messages) {
|
||||
return [
|
||||
'detail' => implode("\n", $messages),
|
||||
'source' => ['pointer' => '/data/attributes/' . $field],
|
||||
];
|
||||
}, array_keys($errors), $errors);
|
||||
|
||||
return new ResponseBag($status, $errors);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Core\Exception\InvalidConfirmationTokenException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class InvalidConfirmationTokenExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof InvalidConfirmationTokenException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 403;
|
||||
$error = ['code' => 'invalid_confirmation_token'];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class ModelNotFoundExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof ModelNotFoundException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 404;
|
||||
$error = [];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Core\Exception\PermissionDeniedException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class PermissionDeniedExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof PermissionDeniedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 401;
|
||||
$error = [];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Api\Handler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Core\Exception\ValidationException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class ValidationExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof ValidationException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 422;
|
||||
|
||||
$messages = $e->getMessages();
|
||||
$errors = array_map(function ($path, $detail) {
|
||||
$source = ['pointer' => '/data/attributes/' . $path];
|
||||
|
||||
return compact('source', 'detail');
|
||||
}, array_keys($messages), $messages);
|
||||
|
||||
return new ResponseBag($status, $errors);
|
||||
}
|
||||
}
|
|
@ -10,32 +10,24 @@
|
|||
|
||||
namespace Flarum\Api\Middleware;
|
||||
|
||||
use Flarum\Api\JsonApiResponse;
|
||||
use Flarum\Foundation\Application;
|
||||
use Illuminate\Contracts\Validation\ValidationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Api\ErrorHandler;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Zend\Stratigility\ErrorMiddlewareInterface;
|
||||
use Flarum\Core;
|
||||
use Exception;
|
||||
|
||||
class HandleErrors implements ErrorMiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
* @var ErrorHandler
|
||||
*/
|
||||
private $app;
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
* @param ErrorHandler $errorHandler
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
public function __construct(ErrorHandler $errorHandler)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,47 +35,6 @@ class HandleErrors implements ErrorMiddlewareInterface
|
|||
*/
|
||||
public function __invoke($e, Request $request, Response $response, callable $out = null)
|
||||
{
|
||||
return $this->handle($e);
|
||||
}
|
||||
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
if ($e instanceof JsonApiSerializableInterface) {
|
||||
$status = $e->getStatusCode();
|
||||
|
||||
$errors = $e->getErrors();
|
||||
} elseif ($e instanceof ValidationException) {
|
||||
$status = 422;
|
||||
|
||||
$errors = $e->errors()->toArray();
|
||||
$errors = array_map(function ($field, $messages) {
|
||||
return [
|
||||
'detail' => implode("\n", $messages),
|
||||
'source' => ['pointer' => '/data/attributes/' . $field],
|
||||
];
|
||||
}, array_keys($errors), $errors);
|
||||
} elseif ($e instanceof ModelNotFoundException) {
|
||||
$status = 404;
|
||||
|
||||
$errors = [];
|
||||
} else {
|
||||
$status = 500;
|
||||
|
||||
$error = [
|
||||
'code' => $status,
|
||||
'title' => 'Internal Server Error'
|
||||
];
|
||||
|
||||
if ($this->app->inDebugMode()) {
|
||||
$error['detail'] = (string) $e;
|
||||
}
|
||||
|
||||
$errors = [$error];
|
||||
}
|
||||
|
||||
$document = new Document;
|
||||
$document->setErrors($errors);
|
||||
|
||||
return new JsonApiResponse($document, $status);
|
||||
return $this->errorHandler->handle($e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,23 +11,7 @@
|
|||
namespace Flarum\Core\Exception;
|
||||
|
||||
use Exception;
|
||||
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
|
||||
|
||||
class FloodingException extends Exception implements JsonApiSerializableInterface
|
||||
class FloodingException extends Exception
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return 429;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,23 +11,7 @@
|
|||
namespace Flarum\Core\Exception;
|
||||
|
||||
use Exception;
|
||||
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
|
||||
|
||||
class InvalidConfirmationTokenException extends Exception implements JsonApiSerializableInterface
|
||||
class InvalidConfirmationTokenException extends Exception
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return 403;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return ['code' => 'invalid_confirmation_token'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,28 +11,7 @@
|
|||
namespace Flarum\Core\Exception;
|
||||
|
||||
use Exception;
|
||||
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
|
||||
|
||||
class PermissionDeniedException extends Exception implements JsonApiSerializableInterface
|
||||
class PermissionDeniedException extends Exception
|
||||
{
|
||||
/**
|
||||
* Return the HTTP status code to be used for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return 401;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of errors, formatted as JSON-API error objects.
|
||||
*
|
||||
* @see http://jsonapi.org/format/#error-objects
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,8 @@
|
|||
namespace Flarum\Core\Exception;
|
||||
|
||||
use Exception;
|
||||
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
|
||||
|
||||
class ValidationException extends Exception implements JsonApiSerializableInterface
|
||||
class ValidationException extends Exception
|
||||
{
|
||||
protected $messages;
|
||||
|
||||
|
@ -28,24 +27,4 @@ class ValidationException extends Exception implements JsonApiSerializableInterf
|
|||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return 422;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return array_map(function ($path, $detail) {
|
||||
$source = ['pointer' => '/data/attributes/' . $path];
|
||||
|
||||
return compact('source', 'detail');
|
||||
}, array_keys($this->messages), $this->messages);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user