mirror of
https://github.com/flarum/framework.git
synced 2025-03-13 22:11:18 +08:00
Merge pull request #1843 from flarum/fl/1641-exception-handling
Implement new error handling stack
This commit is contained in:
commit
fcb3921a42
@ -16,6 +16,10 @@ use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\ErrorHandling\ViewRenderer;
|
||||
use Flarum\Foundation\ErrorHandling\WhoopsRenderer;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
@ -51,11 +55,11 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
// All requests should first be piped through our global error handler
|
||||
if ($app->inDebugMode()) {
|
||||
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class));
|
||||
} else {
|
||||
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class));
|
||||
}
|
||||
$pipe->pipe(new HttpMiddleware\HandleErrors(
|
||||
$app->make(Registry::class),
|
||||
$app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class),
|
||||
$app->tagged(Reporter::class)
|
||||
));
|
||||
|
||||
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
|
||||
|
@ -20,12 +20,13 @@ use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Event\ConfigureNotificationTypes;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\ErrorHandling\JsonApiRenderer;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Tobscure\JsonApi\ErrorHandler;
|
||||
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class ApiServiceProvider extends AbstractServiceProvider
|
||||
@ -49,7 +50,11 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$this->app->singleton('flarum.api.middleware', function (Application $app) {
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
$pipe->pipe($app->make(Middleware\HandleErrors::class));
|
||||
$pipe->pipe(new HttpMiddleware\HandleErrors(
|
||||
$app->make(Registry::class),
|
||||
$app->make(JsonApiRenderer::class),
|
||||
$app->tagged(Reporter::class)
|
||||
));
|
||||
|
||||
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
|
||||
$pipe->pipe($app->make(Middleware\FakeHttpMethods::class));
|
||||
@ -68,25 +73,6 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$this->app->afterResolving('flarum.api.middleware', function (MiddlewarePipe $pipe) {
|
||||
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.api.routes')));
|
||||
});
|
||||
|
||||
$this->app->singleton(ErrorHandler::class, function () {
|
||||
$handler = new ErrorHandler;
|
||||
|
||||
$handler->registerHandler(new ExceptionHandler\FloodingExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\IlluminateValidationExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\InvalidAccessTokenExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\InvalidConfirmationTokenExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\MethodNotAllowedExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\ModelNotFoundExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\PermissionDeniedExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\RouteNotFoundExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler);
|
||||
$handler->registerHandler(new InvalidParameterExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\FallbackExceptionHandler($this->app->inDebugMode(), $this->app->make('log')));
|
||||
|
||||
return $handler;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,11 +12,14 @@
|
||||
namespace Flarum\Api;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\ErrorHandling\JsonApiRenderer;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Throwable;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
|
||||
class Client
|
||||
@ -27,18 +30,18 @@ class Client
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var ErrorHandler
|
||||
* @var Registry
|
||||
*/
|
||||
protected $errorHandler;
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param ErrorHandler $errorHandler
|
||||
* @param Registry $registry
|
||||
*/
|
||||
public function __construct(Container $container, ErrorHandler $errorHandler = null)
|
||||
public function __construct(Container $container, Registry $registry)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->errorHandler = $errorHandler;
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,23 +72,14 @@ class Client
|
||||
|
||||
try {
|
||||
return $controller->handle($request);
|
||||
} catch (Exception $e) {
|
||||
if (! $this->errorHandler) {
|
||||
} catch (Throwable $e) {
|
||||
$error = $this->registry->handle($e);
|
||||
|
||||
if ($error->shouldBeReported()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->errorHandler->handle($e);
|
||||
return (new JsonApiRenderer)->format($error, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ErrorHandler $errorHandler
|
||||
* @return Client
|
||||
*/
|
||||
public function setErrorHandler(?ErrorHandler $errorHandler): self
|
||||
{
|
||||
$this->errorHandler = $errorHandler;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
<?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 Throwable;
|
||||
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(Throwable $e)
|
||||
{
|
||||
if (! $e instanceof Exception) {
|
||||
$e = new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$response = $this->errorHandler->handle($e);
|
||||
|
||||
$document = new Document;
|
||||
$document->setErrors($response->getErrors());
|
||||
|
||||
return new JsonApiResponse($document, $response->getStatus());
|
||||
}
|
||||
}
|
@ -12,7 +12,12 @@
|
||||
namespace Flarum\Api\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class InvalidAccessTokenException extends Exception
|
||||
class InvalidAccessTokenException extends Exception implements KnownError
|
||||
{
|
||||
public function getType(): string
|
||||
{
|
||||
return 'invalid_access_token';
|
||||
}
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class FallbackExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $debug;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @param bool $debug
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct($debug, LoggerInterface $logger)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 500;
|
||||
$error = $this->constructError($e, $status);
|
||||
|
||||
$this->logger->error($e);
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $e
|
||||
* @param $status
|
||||
* @return array
|
||||
*/
|
||||
private function constructError(Exception $e, $status)
|
||||
{
|
||||
$error = ['code' => $status, 'title' => 'Internal server error'];
|
||||
|
||||
if ($this->debug) {
|
||||
$error['detail'] = (string) $e;
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Post\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 = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'too_many_requests'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\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 = $this->formatErrors($e->errors());
|
||||
|
||||
return new ResponseBag($status, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $errors
|
||||
* @return array
|
||||
*/
|
||||
protected function formatErrors(array $errors)
|
||||
{
|
||||
$errors = array_map(function ($field, $messages) {
|
||||
return [
|
||||
'status' => '422',
|
||||
'code' => 'validation_error',
|
||||
'detail' => implode("\n", $messages),
|
||||
'source' => ['pointer' => "/data/attributes/$field"]
|
||||
];
|
||||
}, array_keys($errors), $errors);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\Exception\InvalidAccessTokenException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class InvalidAccessTokenExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof InvalidAccessTokenException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 401;
|
||||
$error = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'invalid_access_token'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\User\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 = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'invalid_confirmation_token'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Http\Exception\MethodNotAllowedException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class MethodNotAllowedExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof MethodNotAllowedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 405;
|
||||
$error = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'method_not_allowed'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
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 = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'resource_not_found'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\User\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 = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'permission_denied'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Http\Exception\RouteNotFoundException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class RouteNotFoundExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof RouteNotFoundException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 404;
|
||||
$error = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'route_not_found'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Http\Exception\TokenMismatchException;
|
||||
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||
|
||||
class TokenMismatchExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function manages(Exception $e)
|
||||
{
|
||||
return $e instanceof TokenMismatchException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Exception $e)
|
||||
{
|
||||
$status = 400;
|
||||
$error = [
|
||||
'status' => (string) $status,
|
||||
'code' => 'csrf_token_mismatch'
|
||||
];
|
||||
|
||||
return new ResponseBag($status, [$error]);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
<?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\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\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)
|
||||
{
|
||||
$errors = array_merge(
|
||||
$this->buildErrors($e->getAttributes(), '/data/attributes'),
|
||||
$this->buildErrors($e->getRelationships(), '/data/relationships')
|
||||
);
|
||||
|
||||
return new ResponseBag(422, $errors);
|
||||
}
|
||||
|
||||
private function buildErrors(array $messages, $pointer)
|
||||
{
|
||||
return array_map(function ($path, $detail) use ($pointer) {
|
||||
return [
|
||||
'status' => '422',
|
||||
'code' => 'validation_error',
|
||||
'detail' => $detail,
|
||||
'source' => ['pointer' => $pointer.'/'.$path]
|
||||
];
|
||||
}, array_keys($messages), $messages);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?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\Middleware;
|
||||
|
||||
use Flarum\Api\ErrorHandler;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Throwable;
|
||||
|
||||
class HandleErrors implements Middleware
|
||||
{
|
||||
/**
|
||||
* @var ErrorHandler
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* @param ErrorHandler $errorHandler
|
||||
*/
|
||||
public function __construct(ErrorHandler $errorHandler)
|
||||
{
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all errors that happen during further middleware execution.
|
||||
*/
|
||||
public function process(Request $request, Handler $handler): Response
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Throwable $e) {
|
||||
return $this->errorHandler->handle($e);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@ use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Formatter\Formatter;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\ErrorHandling\ViewRenderer;
|
||||
use Flarum\Foundation\ErrorHandling\WhoopsRenderer;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
@ -61,11 +65,11 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
// All requests should first be piped through our global error handler
|
||||
if ($app->inDebugMode()) {
|
||||
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class));
|
||||
} else {
|
||||
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class));
|
||||
}
|
||||
$pipe->pipe(new HttpMiddleware\HandleErrors(
|
||||
$app->make(Registry::class),
|
||||
$app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class),
|
||||
$app->tagged(Reporter::class)
|
||||
));
|
||||
|
||||
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\CollectGarbage::class));
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?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\Foundation\ErrorHandling\ExceptionHandler;
|
||||
|
||||
use Flarum\Foundation\ErrorHandling\HandledError;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class IlluminateValidationExceptionHandler
|
||||
{
|
||||
public function handle(ValidationException $e): HandledError
|
||||
{
|
||||
return (new HandledError(
|
||||
$e, 'validation_error', 422
|
||||
))->withDetails($this->errorDetails($e));
|
||||
}
|
||||
|
||||
protected function errorDetails(ValidationException $e): array
|
||||
{
|
||||
$errors = $e->errors();
|
||||
|
||||
return array_map(function ($field, $messages) {
|
||||
return [
|
||||
'detail' => implode("\n", $messages),
|
||||
'source' => ['pointer' => "/data/attributes/$field"]
|
||||
];
|
||||
}, array_keys($errors), $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\Foundation\ErrorHandling\ExceptionHandler;
|
||||
|
||||
use Flarum\Foundation\ErrorHandling\HandledError;
|
||||
use Flarum\Foundation\ValidationException;
|
||||
|
||||
class ValidationExceptionHandler
|
||||
{
|
||||
public function handle(ValidationException $e)
|
||||
{
|
||||
return (new HandledError(
|
||||
$e, 'validation_error', 422
|
||||
))->withDetails(array_merge(
|
||||
$this->buildDetails($e->getAttributes(), '/data/attributes'),
|
||||
$this->buildDetails($e->getRelationships(), '/data/relationships')
|
||||
));
|
||||
}
|
||||
|
||||
private function buildDetails(array $messages, $pointer): array
|
||||
{
|
||||
return array_map(function ($path, $detail) use ($pointer) {
|
||||
return [
|
||||
'detail' => $detail,
|
||||
'source' => ['pointer' => $pointer.'/'.$path]
|
||||
];
|
||||
}, array_keys($messages), $messages);
|
||||
}
|
||||
}
|
20
framework/core/src/Foundation/ErrorHandling/Formatter.php
Normal file
20
framework/core/src/Foundation/ErrorHandling/Formatter.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
interface Formatter
|
||||
{
|
||||
public function format(HandledError $error, Request $request): Response;
|
||||
}
|
67
framework/core/src/Foundation/ErrorHandling/HandledError.php
Normal file
67
framework/core/src/Foundation/ErrorHandling/HandledError.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class HandledError
|
||||
{
|
||||
private $error;
|
||||
private $type;
|
||||
private $statusCode;
|
||||
|
||||
private $details = [];
|
||||
|
||||
public static function unknown(Throwable $error)
|
||||
{
|
||||
return new static($error, 'unknown', 500);
|
||||
}
|
||||
|
||||
public function __construct(Throwable $error, $type, $statusCode)
|
||||
{
|
||||
$this->error = $error;
|
||||
$this->type = $type;
|
||||
$this->statusCode = $statusCode;
|
||||
}
|
||||
|
||||
public function withDetails(array $details): self
|
||||
{
|
||||
$this->details = $details;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getError(): Throwable
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function shouldBeReported(): bool
|
||||
{
|
||||
return $this->type === 'unknown';
|
||||
}
|
||||
|
||||
public function getDetails(): array
|
||||
{
|
||||
return $this->details;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Flarum\Api\JsonApiResponse;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class JsonApiRenderer implements Formatter
|
||||
{
|
||||
public function format(HandledError $error, Request $request): Response
|
||||
{
|
||||
$document = new Document;
|
||||
$document->setErrors([
|
||||
[
|
||||
'status' => (string) $error->getStatusCode(),
|
||||
'code' => $error->getType(),
|
||||
],
|
||||
]);
|
||||
|
||||
return new JsonApiResponse($document, $error->getStatusCode());
|
||||
}
|
||||
}
|
32
framework/core/src/Foundation/ErrorHandling/LogReporter.php
Normal file
32
framework/core/src/Foundation/ErrorHandling/LogReporter.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class LogReporter implements Reporter
|
||||
{
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function report(HandledError $error)
|
||||
{
|
||||
$this->logger->error($error->getError());
|
||||
}
|
||||
}
|
73
framework/core/src/Foundation/ErrorHandling/Registry.php
Normal file
73
framework/core/src/Foundation/ErrorHandling/Registry.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Flarum\Foundation\KnownError;
|
||||
use Throwable;
|
||||
|
||||
class Registry
|
||||
{
|
||||
private $statusMap;
|
||||
private $classMap;
|
||||
private $handlerMap;
|
||||
|
||||
public function __construct(array $statusMap, array $classMap, array $handlerMap)
|
||||
{
|
||||
$this->statusMap = $statusMap;
|
||||
$this->classMap = $classMap;
|
||||
$this->handlerMap = $handlerMap;
|
||||
}
|
||||
|
||||
public function handle(Throwable $error): HandledError
|
||||
{
|
||||
return $this->handleKnownTypes($error)
|
||||
?? $this->handleCustomTypes($error)
|
||||
?? HandledError::unknown($error);
|
||||
}
|
||||
|
||||
private function handleKnownTypes(Throwable $error): ?HandledError
|
||||
{
|
||||
$errorType = null;
|
||||
|
||||
if ($error instanceof KnownError) {
|
||||
$errorType = $error->getType();
|
||||
} else {
|
||||
$errorClass = get_class($error);
|
||||
if (isset($this->classMap[$errorClass])) {
|
||||
$errorType = $this->classMap[$errorClass];
|
||||
}
|
||||
}
|
||||
|
||||
if ($errorType) {
|
||||
return new HandledError(
|
||||
$error,
|
||||
$errorType,
|
||||
$this->statusMap[$errorType] ?? 500
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function handleCustomTypes(Throwable $error): ?HandledError
|
||||
{
|
||||
$errorClass = get_class($error);
|
||||
|
||||
if (isset($this->handlerMap[$errorClass])) {
|
||||
$handler = new $this->handlerMap[$errorClass];
|
||||
|
||||
return $handler->handle($error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
17
framework/core/src/Foundation/ErrorHandling/Reporter.php
Normal file
17
framework/core/src/Foundation/ErrorHandling/Reporter.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
interface Reporter
|
||||
{
|
||||
public function report(HandledError $error);
|
||||
}
|
78
framework/core/src/Foundation/ErrorHandling/ViewRenderer.php
Normal file
78
framework/core/src/Foundation/ErrorHandling/ViewRenderer.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
|
||||
class ViewRenderer implements Formatter
|
||||
{
|
||||
/**
|
||||
* @var ViewFactory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
public function __construct(ViewFactory $view, TranslatorInterface $translator, SettingsRepositoryInterface $settings)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->translator = $translator;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function format(HandledError $error, Request $request): Response
|
||||
{
|
||||
$view = $this->view->make($this->determineView($error))
|
||||
->with('error', $error->getError())
|
||||
->with('message', $this->getMessage($error));
|
||||
|
||||
return new HtmlResponse($view->render(), $error->getStatusCode());
|
||||
}
|
||||
|
||||
private function determineView(HandledError $error): string
|
||||
{
|
||||
$view = [
|
||||
'route_not_found' => '404',
|
||||
'csrf_token_mismatch' => '419',
|
||||
][$error->getType()] ?? 'default';
|
||||
|
||||
return "flarum.forum::error.$view";
|
||||
}
|
||||
|
||||
private function getMessage(HandledError $error)
|
||||
{
|
||||
return $this->getTranslationIfExists($error->getStatusCode())
|
||||
?? $this->getTranslationIfExists('unknown')
|
||||
?? 'An error occurred while trying to load this page.';
|
||||
}
|
||||
|
||||
private function getTranslationIfExists(string $errorType)
|
||||
{
|
||||
$key = "core.views.error.${errorType}_message";
|
||||
$translation = $this->translator->trans($key, ['{forum}' => $this->settings->get('forum_title')]);
|
||||
|
||||
return $translation === $key ? null : $translation;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?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\Foundation\ErrorHandling;
|
||||
|
||||
use Franzl\Middleware\Whoops\WhoopsRunner;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class WhoopsRenderer implements Formatter
|
||||
{
|
||||
public function format(HandledError $error, Request $request): Response
|
||||
{
|
||||
return WhoopsRunner::handle($error->getError(), $request)
|
||||
->withStatus($error->getStatusCode());
|
||||
}
|
||||
}
|
76
framework/core/src/Foundation/ErrorServiceProvider.php
Normal file
76
framework/core/src/Foundation/ErrorServiceProvider.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?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\Foundation;
|
||||
|
||||
use Flarum\Foundation\ErrorHandling\ExceptionHandler;
|
||||
use Flarum\Foundation\ErrorHandling\LogReporter;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Validation\ValidationException as IlluminateValidationException;
|
||||
use Tobscure\JsonApi\Exception\InvalidParameterException;
|
||||
|
||||
class ErrorServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('flarum.error.statuses', function () {
|
||||
return [
|
||||
// 400 Bad Request
|
||||
'csrf_token_mismatch' => 400,
|
||||
'invalid_parameter' => 400,
|
||||
|
||||
// 401 Unauthorized
|
||||
'invalid_access_token' => 401,
|
||||
|
||||
// 403 Forbidden
|
||||
'forbidden' => 403,
|
||||
'invalid_confirmation_token' => 403,
|
||||
'permission_denied' => 403,
|
||||
|
||||
// 404 Not Found
|
||||
'model_not_found' => 404,
|
||||
'route_not_found' => 404,
|
||||
|
||||
// 405 Method Not Allowed
|
||||
'method_not_allowed' => 405,
|
||||
|
||||
// 429 Too Many Requests
|
||||
'too_many_requests' => 429,
|
||||
];
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.error.classes', function () {
|
||||
return [
|
||||
InvalidParameterException::class => 'invalid_parameter',
|
||||
ModelNotFoundException::class => 'model_not_found',
|
||||
];
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.error.handlers', function () {
|
||||
return [
|
||||
IlluminateValidationException::class => ExceptionHandler\IlluminateValidationExceptionHandler::class,
|
||||
ValidationException::class => ExceptionHandler\ValidationExceptionHandler::class,
|
||||
];
|
||||
});
|
||||
|
||||
$this->app->singleton(Registry::class, function () {
|
||||
return new Registry(
|
||||
$this->app->make('flarum.error.statuses'),
|
||||
$this->app->make('flarum.error.classes'),
|
||||
$this->app->make('flarum.error.handlers')
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->tag(LogReporter::class, Reporter::class);
|
||||
}
|
||||
}
|
@ -117,6 +117,7 @@ class InstalledSite implements SiteInterface
|
||||
$laravel->register(DatabaseServiceProvider::class);
|
||||
$laravel->register(DiscussionServiceProvider::class);
|
||||
$laravel->register(ExtensionServiceProvider::class);
|
||||
$laravel->register(ErrorServiceProvider::class);
|
||||
$laravel->register(FilesystemServiceProvider::class);
|
||||
$laravel->register(FormatterServiceProvider::class);
|
||||
$laravel->register(ForumServiceProvider::class);
|
||||
|
17
framework/core/src/Foundation/KnownError.php
Normal file
17
framework/core/src/Foundation/KnownError.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?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\Foundation;
|
||||
|
||||
interface KnownError
|
||||
{
|
||||
public function getType(): string;
|
||||
}
|
@ -69,6 +69,7 @@ class UninstalledSite implements SiteInterface
|
||||
|
||||
$this->registerLogger($laravel);
|
||||
|
||||
$laravel->register(ErrorServiceProvider::class);
|
||||
$laravel->register(LocaleServiceProvider::class);
|
||||
$laravel->register(FilesystemServiceProvider::class);
|
||||
$laravel->register(SessionServiceProvider::class);
|
||||
|
@ -70,8 +70,6 @@ class Frontend
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
$this->api->setErrorHandler(null);
|
||||
|
||||
return $this->getResponseBody(
|
||||
$this->api->send(ShowForumController::class, $actor)
|
||||
);
|
||||
|
@ -12,11 +12,12 @@
|
||||
namespace Flarum\Http\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class ForbiddenException extends Exception
|
||||
class ForbiddenException extends Exception implements KnownError
|
||||
{
|
||||
public function __construct($message = null, $code = 403, Exception $previous = null)
|
||||
public function getType(): string
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
return 'forbidden';
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
namespace Flarum\Http\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class MethodNotAllowedException extends Exception
|
||||
class MethodNotAllowedException extends Exception implements KnownError
|
||||
{
|
||||
public function __construct($message = null, $code = 405, Exception $previous = null)
|
||||
public function getType(): string
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
return 'method_not_allowed';
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
namespace Flarum\Http\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class RouteNotFoundException extends Exception
|
||||
class RouteNotFoundException extends Exception implements KnownError
|
||||
{
|
||||
public function __construct($message = null, $code = 404, Exception $previous = null)
|
||||
public function getType(): string
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
return 'route_not_found';
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
namespace Flarum\Http\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class TokenMismatchException extends Exception
|
||||
class TokenMismatchException extends Exception implements KnownError
|
||||
{
|
||||
public function __construct($message = null, $code = 419, Exception $previous = null)
|
||||
public function getType(): string
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
return 'csrf_token_mismatch';
|
||||
}
|
||||
}
|
||||
|
@ -11,27 +11,36 @@
|
||||
|
||||
namespace Flarum\Http\Middleware;
|
||||
|
||||
use Franzl\Middleware\Whoops\WhoopsRunner;
|
||||
use Flarum\Foundation\ErrorHandling\Formatter;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
|
||||
class HandleErrorsWithWhoops implements Middleware
|
||||
class HandleErrors implements Middleware
|
||||
{
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
* @var Registry
|
||||
*/
|
||||
protected $logger;
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $logger
|
||||
* @var Formatter
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger)
|
||||
protected $formatter;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Foundation\ErrorHandling\Reporter[]
|
||||
*/
|
||||
protected $reporters;
|
||||
|
||||
public function __construct(Registry $registry, Formatter $formatter, array $reporters)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->registry = $registry;
|
||||
$this->formatter = $formatter;
|
||||
$this->reporters = $reporters;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,19 +51,15 @@ class HandleErrorsWithWhoops implements Middleware
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Throwable $e) {
|
||||
$status = 500;
|
||||
$errorCode = $e->getCode();
|
||||
$error = $this->registry->handle($e);
|
||||
|
||||
if ($errorCode !== 404) {
|
||||
$this->logger->error($e);
|
||||
if ($error->shouldBeReported()) {
|
||||
foreach ($this->reporters as $reporter) {
|
||||
$reporter->report($error);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_int($errorCode) && $errorCode >= 400 && $errorCode < 600) {
|
||||
$status = $errorCode;
|
||||
}
|
||||
|
||||
return WhoopsRunner::handle($e, $request)
|
||||
->withStatus($status);
|
||||
return $this->formatter->format($error, $request);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
<?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\Http\Middleware;
|
||||
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Throwable;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
|
||||
class HandleErrorsWithView implements Middleware
|
||||
{
|
||||
/**
|
||||
* @var ViewFactory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @param ViewFactory $view
|
||||
* @param LoggerInterface $logger
|
||||
* @param TranslatorInterface $translator
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
*/
|
||||
public function __construct(ViewFactory $view, LoggerInterface $logger, TranslatorInterface $translator, SettingsRepositoryInterface $settings)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->logger = $logger;
|
||||
$this->translator = $translator;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all errors that happen during further middleware execution.
|
||||
*/
|
||||
public function process(Request $request, Handler $handler): Response
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Throwable $e) {
|
||||
return $this->formatException($e);
|
||||
}
|
||||
}
|
||||
|
||||
protected function formatException(Throwable $error)
|
||||
{
|
||||
$status = 500;
|
||||
$errorCode = $error->getCode();
|
||||
|
||||
// If it seems to be a valid HTTP status code, we pass on the
|
||||
// exception's status.
|
||||
if (is_int($errorCode) && $errorCode >= 400 && $errorCode < 600) {
|
||||
$status = $errorCode;
|
||||
}
|
||||
|
||||
if (! $this->view->exists($name = "flarum.forum::error.$status")) {
|
||||
$name = 'flarum.forum::error.default';
|
||||
|
||||
$this->logger->error($error);
|
||||
}
|
||||
|
||||
$view = $this->view->make($name)
|
||||
->with('error', $error)
|
||||
->with('message', $this->getMessage($status));
|
||||
|
||||
return new HtmlResponse($view->render(), $status);
|
||||
}
|
||||
|
||||
private function getMessage($status)
|
||||
{
|
||||
return $this->getTranslationIfExists($status)
|
||||
?? $this->getTranslationIfExists(500)
|
||||
?? 'An error occurred while trying to load this page.';
|
||||
}
|
||||
|
||||
private function getTranslationIfExists($status)
|
||||
{
|
||||
$key = "core.views.error.${status}_message";
|
||||
$translation = $this->translator->trans($key, ['{forum}' => $this->settings->get('forum_title')]);
|
||||
|
||||
return $translation === $key ? null : $translation;
|
||||
}
|
||||
}
|
@ -12,8 +12,11 @@
|
||||
namespace Flarum\Install;
|
||||
|
||||
use Flarum\Foundation\AppInterface;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\ErrorHandling\WhoopsRenderer;
|
||||
use Flarum\Http\Middleware\DispatchRoute;
|
||||
use Flarum\Http\Middleware\HandleErrorsWithWhoops;
|
||||
use Flarum\Http\Middleware\HandleErrors;
|
||||
use Flarum\Http\Middleware\StartSession;
|
||||
use Flarum\Install\Console\InstallCommand;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
@ -37,7 +40,11 @@ class Installer implements AppInterface
|
||||
public function getRequestHandler()
|
||||
{
|
||||
$pipe = new MiddlewarePipe;
|
||||
$pipe->pipe($this->container->make(HandleErrorsWithWhoops::class));
|
||||
$pipe->pipe(new HandleErrors(
|
||||
$this->container->make(Registry::class),
|
||||
$this->container->make(WhoopsRenderer::class),
|
||||
$this->container->tagged(Reporter::class)
|
||||
));
|
||||
$pipe->pipe($this->container->make(StartSession::class));
|
||||
$pipe->pipe(
|
||||
new DispatchRoute($this->container->make('flarum.install.routes'))
|
||||
|
@ -12,7 +12,12 @@
|
||||
namespace Flarum\Post\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class FloodingException extends Exception
|
||||
class FloodingException extends Exception implements KnownError
|
||||
{
|
||||
public function getType(): string
|
||||
{
|
||||
return 'too_many_requests';
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,12 @@
|
||||
namespace Flarum\User\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class InvalidConfirmationTokenException extends Exception
|
||||
class InvalidConfirmationTokenException extends Exception implements KnownError
|
||||
{
|
||||
public function getType(): string
|
||||
{
|
||||
return 'invalid_confirmation_token';
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
namespace Flarum\User\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class PermissionDeniedException extends Exception
|
||||
class PermissionDeniedException extends Exception implements KnownError
|
||||
{
|
||||
public function __construct($message = null, $code = 403, Exception $previous = null)
|
||||
public function getType(): string
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
return 'permission_denied';
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\CreateGroupController;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\Guest;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Str;
|
||||
@ -63,11 +62,10 @@ class AuthenticateWithApiKeyTest extends TestCase
|
||||
{
|
||||
/** @var Client $api */
|
||||
$api = $this->app()->getContainer()->make(Client::class);
|
||||
$api->setErrorHandler(null);
|
||||
|
||||
$this->expectException(PermissionDeniedException::class);
|
||||
$response = $api->send(CreateGroupController::class, new Guest);
|
||||
|
||||
$api->send(CreateGroupController::class, new Guest);
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,8 +53,6 @@ abstract class ApiControllerTestCase extends TestCase
|
||||
/** @var Client $api */
|
||||
$api = $this->app()->getContainer()->make(Client::class);
|
||||
|
||||
$api->setErrorHandler(null);
|
||||
|
||||
return $api->send($controller, $actor ?? new Guest, $queryParams, $body);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ use Flarum\Api\Controller\CreateDiscussionController;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateDiscussionControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
@ -72,11 +71,9 @@ class CreateDiscussionControllerTest extends ApiControllerTestCase
|
||||
$this->actor = User::find(1);
|
||||
|
||||
$data = Arr::except($this->data, 'content');
|
||||
$response = $this->callWith($data);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$this->expectExceptionMessage('The given data was invalid.');
|
||||
|
||||
$this->callWith($data);
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,10 +84,8 @@ class CreateDiscussionControllerTest extends ApiControllerTestCase
|
||||
$this->actor = User::find(1);
|
||||
|
||||
$data = Arr::except($this->data, 'title');
|
||||
$response = $this->callWith($data);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$this->expectExceptionMessage('The given data was invalid.');
|
||||
|
||||
$this->callWith($data);
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,9 @@ namespace Flarum\Tests\integration\api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\CreateGroupController;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateGroupControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
@ -55,10 +53,7 @@ class CreateGroupControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
$this->actor = User::find(1);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$this->expectExceptionMessage('The given data was invalid.');
|
||||
|
||||
$this->callWith();
|
||||
$this->assertEquals(422, $this->callWith()->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,8 +84,6 @@ class CreateGroupControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
$this->actor = User::find(2);
|
||||
|
||||
$this->expectException(PermissionDeniedException::class);
|
||||
|
||||
$this->callWith($this->data);
|
||||
$this->assertEquals(403, $this->callWith($this->data)->getStatusCode());
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,8 @@ namespace Flarum\Tests\integration\api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\CreateUserController;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateUserControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
@ -53,10 +51,9 @@ class CreateUserControllerTest extends ApiControllerTestCase
|
||||
*/
|
||||
public function cannot_create_user_without_data()
|
||||
{
|
||||
$this->expectException(ValidationException::class);
|
||||
$this->expectExceptionMessage('The given data was invalid.');
|
||||
$response = $this->callWith();
|
||||
|
||||
$this->callWith();
|
||||
$this->assertEquals(422, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,12 +103,9 @@ class CreateUserControllerTest extends ApiControllerTestCase
|
||||
$settings = app(SettingsRepositoryInterface::class);
|
||||
$settings->set('allow_sign_up', false);
|
||||
|
||||
$this->expectException(PermissionDeniedException::class);
|
||||
$response = $this->callWith($this->data);
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
try {
|
||||
$this->callWith($this->data);
|
||||
} finally {
|
||||
$settings->set('allow_sign_up', true);
|
||||
}
|
||||
$settings->set('allow_sign_up', true);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace Flarum\Tests\integration\api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\ListNotificationsController;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\User;
|
||||
|
||||
class ListNotificationsControllerTest extends ApiControllerTestCase
|
||||
@ -35,9 +34,9 @@ class ListNotificationsControllerTest extends ApiControllerTestCase
|
||||
*/
|
||||
public function disallows_index_for_guest()
|
||||
{
|
||||
$this->expectException(PermissionDeniedException::class);
|
||||
$response = $this->callWith();
|
||||
|
||||
$this->callWith();
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace Flarum\Tests\integration\api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\ListUsersController;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\User;
|
||||
|
||||
class ListUsersControllerTest extends ApiControllerTestCase
|
||||
@ -41,9 +40,9 @@ class ListUsersControllerTest extends ApiControllerTestCase
|
||||
*/
|
||||
public function disallows_index_for_guest()
|
||||
{
|
||||
$this->expectException(PermissionDeniedException::class);
|
||||
$response = $this->callWith();
|
||||
|
||||
$this->callWith();
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,6 @@ use Carbon\Carbon;
|
||||
use Flarum\Api\Controller\ShowDiscussionController;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class ShowDiscussionControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
@ -73,11 +72,9 @@ class ShowDiscussionControllerTest extends ApiControllerTestCase
|
||||
*/
|
||||
public function guest_cannot_see_empty_discussion()
|
||||
{
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
|
||||
$response = $this->callWith([], ['id' => 1]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,8 +92,8 @@ class ShowDiscussionControllerTest extends ApiControllerTestCase
|
||||
*/
|
||||
public function guests_cannot_see_private_discussion()
|
||||
{
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
$response = $this->callWith([], ['id' => 3]);
|
||||
|
||||
$this->callWith([], ['id' => 3]);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\FloodingExceptionHandler;
|
||||
use Flarum\Post\Exception\FloodingException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FloodingExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new FloodingExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new FloodingException));
|
||||
}
|
||||
|
||||
public function test_it_provides_expected_output()
|
||||
{
|
||||
$result = $this->handler->handle(new FloodingException);
|
||||
|
||||
$this->assertEquals(429, $result->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '429',
|
||||
'code' => 'too_many_requests'
|
||||
]
|
||||
], $result->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\Exception\InvalidAccessTokenException;
|
||||
use Flarum\Api\ExceptionHandler\InvalidAccessTokenExceptionHandler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class InvalidAccessTokenExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new InvalidAccessTokenExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new InvalidAccessTokenException));
|
||||
}
|
||||
|
||||
public function test_output()
|
||||
{
|
||||
$response = $this->handler->handle(new InvalidAccessTokenException);
|
||||
|
||||
$this->assertEquals(401, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '401',
|
||||
'code' => 'invalid_access_token'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\InvalidConfirmationTokenExceptionHandler;
|
||||
use Flarum\User\Exception\InvalidConfirmationTokenException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class InvalidConfirmationTokenExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new InvalidConfirmationTokenExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new InvalidConfirmationTokenException));
|
||||
}
|
||||
|
||||
public function test_output()
|
||||
{
|
||||
$response = $this->handler->handle(new InvalidConfirmationTokenException);
|
||||
|
||||
$this->assertEquals(403, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '403',
|
||||
'code' => 'invalid_confirmation_token'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\MethodNotAllowedExceptionHandler;
|
||||
use Flarum\Http\Exception\MethodNotAllowedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MethodNotAllowedExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new MethodNotAllowedExceptionHandler();
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new MethodNotAllowedException()));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new MethodNotAllowedException);
|
||||
|
||||
$this->assertEquals(405, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '405',
|
||||
'code' => 'method_not_allowed'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\ModelNotFoundExceptionHandler;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ModelNotFoundExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new ModelNotFoundExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new ModelNotFoundException));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new ModelNotFoundException);
|
||||
|
||||
$this->assertEquals(404, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '404',
|
||||
'code' => 'resource_not_found'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\PermissionDeniedExceptionHandler;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PermissionDeniedExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new PermissionDeniedExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new PermissionDeniedException));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new PermissionDeniedException);
|
||||
|
||||
$this->assertEquals(401, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '401',
|
||||
'code' => 'permission_denied'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\RouteNotFoundExceptionHandler;
|
||||
use Flarum\Http\Exception\RouteNotFoundException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RouteNotFoundExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new RouteNotFoundExceptionHandler();
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new RouteNotFoundException()));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new RouteNotFoundException);
|
||||
|
||||
$this->assertEquals(404, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '404',
|
||||
'code' => 'route_not_found'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
<?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\Tests\unit\Api\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\TokenMismatchExceptionHandler;
|
||||
use Flarum\Http\Exception\TokenMismatchException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TokenMismatchExceptionHandlerTest extends TestCase
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->handler = new TokenMismatchExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new TokenMismatchException()));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new TokenMismatchException);
|
||||
|
||||
$this->assertEquals(400, $response->getStatus());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '400',
|
||||
'code' => 'csrf_token_mismatch'
|
||||
]
|
||||
], $response->getErrors());
|
||||
}
|
||||
}
|
@ -9,10 +9,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\unit\Api\ExceptionHandler;
|
||||
namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\IlluminateValidationExceptionHandler;
|
||||
use Flarum\Foundation\ErrorHandling\ExceptionHandler\IlluminateValidationExceptionHandler;
|
||||
use Illuminate\Translation\ArrayLoader;
|
||||
use Illuminate\Translation\Translator;
|
||||
use Illuminate\Validation\Factory;
|
||||
@ -28,29 +27,20 @@ class IlluminateValidationExceptionHandlerTest extends TestCase
|
||||
$this->handler = new IlluminateValidationExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_familiar_exceptions()
|
||||
{
|
||||
$validException = new ValidationException($this->makeValidator());
|
||||
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages($validException));
|
||||
}
|
||||
|
||||
public function test_it_creates_the_desired_output()
|
||||
{
|
||||
$exception = new ValidationException($this->makeValidator(['foo' => ''], ['foo' => 'required']));
|
||||
|
||||
$response = $this->handler->handle($exception);
|
||||
$error = $this->handler->handle($exception);
|
||||
|
||||
$this->assertEquals(422, $response->getStatus());
|
||||
$this->assertEquals(422, $error->getStatusCode());
|
||||
$this->assertEquals('validation_error', $error->getType());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '422',
|
||||
'code' => 'validation_error',
|
||||
'detail' => 'validation.required',
|
||||
'source' => ['pointer' => '/data/attributes/foo']
|
||||
]
|
||||
], $response->getErrors());
|
||||
], $error->getDetails());
|
||||
}
|
||||
|
||||
private function makeValidator($data = [], $rules = [])
|
@ -9,10 +9,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\unit\Api\ExceptionHandler;
|
||||
namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ExceptionHandler\ValidationExceptionHandler;
|
||||
use Flarum\Foundation\ErrorHandling\ExceptionHandler\ValidationExceptionHandler;
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@ -25,33 +24,24 @@ class ValidationExceptionHandlerTest extends TestCase
|
||||
$this->handler = new ValidationExceptionHandler;
|
||||
}
|
||||
|
||||
public function test_it_handles_recognisable_exceptions()
|
||||
{
|
||||
$this->assertFalse($this->handler->manages(new Exception));
|
||||
$this->assertTrue($this->handler->manages(new ValidationException([])));
|
||||
}
|
||||
|
||||
public function test_managing_exceptions()
|
||||
{
|
||||
$response = $this->handler->handle(new ValidationException(
|
||||
$error = $this->handler->handle(new ValidationException(
|
||||
['foo' => 'Attribute error'],
|
||||
['bar' => 'Relationship error']
|
||||
));
|
||||
|
||||
$this->assertEquals(422, $response->getStatus());
|
||||
$this->assertEquals(422, $error->getStatusCode());
|
||||
$this->assertEquals('validation_error', $error->getType());
|
||||
$this->assertEquals([
|
||||
[
|
||||
'status' => '422',
|
||||
'code' => 'validation_error',
|
||||
'detail' => 'Attribute error',
|
||||
'source' => ['pointer' => '/data/attributes/foo']
|
||||
],
|
||||
[
|
||||
'status' => '422',
|
||||
'code' => 'validation_error',
|
||||
'detail' => 'Relationship error',
|
||||
'source' => ['pointer' => '/data/relationships/bar']
|
||||
]
|
||||
], $response->getErrors());
|
||||
], $error->getDetails());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user