Closed lautiamkok closed 5 years ago
By calling the handle()
method of the handler in the catch-block again, the handler would throw the exception again. But this time, no one is there to catch this second exception.
In Slim 4 you could either define your custom error handler (and pass it to the ErrorMiddleware
) or simply define custom error renderers (based on accepted content-types by your request or on a forced content-type).
More information can be found in the docs, currently in development: https://github.com/slimphp/Slim-Website/blob/gh-pages-4.x/docs/v4/middleware/error-handling.md
@adriansuter none of that actually works. giving up...
In my view, this should be made simpler. But it is way too complicated now.
@lautiamkok it does work, I think you're doing something wrong on your end. Where are you adding this middleware? It should be the very last middleware you add, right before that middleware should be the routing middleware, otherwise Slim automatically appends the RoutingMiddleware
if it hasn't been appended manually, which means it would go in front of your error handling middleware, which means you wouldn't catch anything.
@l0gicgate let me check later again and get back to you later. there are some errors in the code in the doc by the way
slim should have left all the error handling alone from the year dot. how it handles errors just does not make sense. for example in slim 3.12, if i want to handle 404 and other errors myself, I have to write two blocks of code:
$container = new \Slim\Container();
$container['errorHandler'] = function ($container) {
return function ($request, $response, $exception) use ($container) {
// print_r(get_class_methods($exception));
$message = $exception->getMessage();
$code = $exception->getCode();
$data = [
"status" => $code,
"message" => $message
];
$payload = json_encode($data);
$response->getBody()->write($payload);
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($code);
};
};
$container['notFoundHandler'] = function ($container) {
return function ($request, $response) use ($container) {
return $response->withStatus(404)
->withHeader('Content-Type', 'text/html')
->write('Page not found');
};
};
It should just be one block of code. also, errorHandler
and notFoundHandler
work differently. they should work the same way.
My personal view on these lines below for handling error in slim 4:
$callableResolver = $app->getCallableResolver();
$responseFactory = $app->getResponseFactory();
$errorMiddleware = new ErrorMiddleware($callableResolver, $responseFactory, true, true, true);
$app->add($errorMiddleware);
I am not sure if this is a PSR standard or not but it is too ugly to read and to understand what is going on.
I don't know what these two $callableResolver
and $responseFactory
are doing there. if they are not helping us to understand by reading them, they shouldn't be there.
this is just my opinion.
That is dependency injection. As the middleware needs to have the callable resolver and the response factory, they would be given to the middleware. That way, the middleware is as independent as possible from the rest.
I’m closing this as resolved. The docs are clear. If you dislike the architecture you can always use a different framework. You’re also more than welcome to contribute when those architecture decisions are being made, the PRs and discussions are public.
Another way to handle this is to define a default route after all your other route definitions:
$app->any('{route:.*}', function(Request $request, Response $response) {
$response = $response->withStatus(404, 'page not found');
return $response;
});
I think a good answer would be to show exactly how a way to disable ALL error handling of any kind since I'm sure slim is already using specific exception types internally that can easily be caught. Or show how to specify a single error handler for all events so a controller view can be used and just display a 404/500 or whatever else message it needs to show accordingly.
Just saying docs are clear with no reference and if you don't like it move along isn't a great response.
If there is no way to do this I'd agree it is just too complicated. Error handling is so simple.
I'm unsuccessfully searching for a way to achieve what was so easily possible in Slim 3 – catching any kind of exception caused when calling a route, handling it myself and then displaying a JSON response with a custom HTTP status code, like mentioned in https://github.com/slimphp/Slim/issues/2757#issuecomment-512360043.
Could anybody please point me into the right direction, it's crazy that this standard feature is not even mentioned in the docs. Thanks.
@skizzo It is possible. Just don't add the ErrorHandlingMiddleware and replace it with your own middleware that catches all Throwables and/or other more specific Exceptions.
@odan , sorry but your "now draw the rest of the f***king owl" answer is not helpful at all. The issue subject literally says "how" and not "if" this is possible, and nobody here even bothered.
So in any case anybody else stumbles over this issue after hours of searching the web (for something that should be properly documented in the first place), I finally got it working like this in Slim 4.14
(PHP 8.x
):
We need to install a PSR-7 implementation, e.g.nyholm/psr7
with composer require nyholm/psr7
.
use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Interfaces\ErrorHandlerInterface;
class NBSlimErrorHandler implements ErrorHandlerInterface
{
// invoked from outside of Slim
public static function logException(Throwable $exception)
{
$statusCode = 500; // or anything else
$errorData = NBSlimErrorHandler::getExceptionData($exception);
// feel free to use your own logger here
NBLogger::error($errorData["message"], [
"message" => $errorData["message"],
"file" => $errorData["file"],
"line" => $errorData["line"],
"trace" => $errorData["trace"],
]);
header('Content-Type: application/json');
http_response_code(500);
echo json_encode([
"success" => false,
"error" => $errorData,
]);
exit();
}
// invoked by Slim
public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface
{
$statusCode = 500; // or anything else
$errorData = NBSlimErrorHandler::getExceptionData($exception);
// feel free to use your own logger here
NBLogger::error($errorData["message"], [
"message" => $errorData["message"],
"file" => $errorData["file"],
"line" => $errorData["line"],
"trace" => $errorData["trace"],
]);
$psr17Factory = new Psr17Factory();
$response = $psr17Factory->createResponse($statusCode);
$response->getBody()->write(json_encode([
"success" => false,
"error" => $errorData,
]));
return $response->withStatus($statusCode)->withHeader('Content-Type', 'application/json');
}
// Helper Function to extract data from an exception
public static function getExceptionData(Throwable $exception)
{
$stripPath = getPublicHtmlFolder(); // see below
$message = $exception->getMessage();
$file = implode("", explode($stripPath, $exception->getFile())); // remove $stripPath
$line = $exception->getLine();
$trace = $exception->getTraceAsString();
$trace = explode("\n", substr(implode("", explode($stripPath, $trace)), 1));
return [
"message" => $message,
"file" => $file,
"line" => $line,
"trace" => $trace,
];
}
}
// for errors outside of a Slim Route handler
function log_exception(Throwable $exception)
{
NBSlimErrorHandler::logException($exception);
}
set_exception_handler("log_exception");
ini_set("display_errors", "off");
error_reporting(E_ALL);
// helper function to determine the public_html folder
function getPublicHtmlFolder()
{
$dirname = dirname(__DIR__);
$search = "/public_html/";
$pathPublicHtml = mb_substr($dirname, 0, mb_strpos($dirname, $search) + mb_strlen($search));
return $pathPublicHtml;
}
// ...
$app = AppFactory::create();
$app->setBasePath("/your/base/path");
// Use custom Error Handler
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler(NBSlimErrorHandler::class);
// Add Routing Middleware
$app->addRoutingMiddleware();
// ...
@skizzo
I apologize for my tone, but it's just frustrating that issues like these are being closed with reasoning that sounds like "RTFM" when the docs obviously don't explain such a basic use case scenario – which is why this issue was created in the first place.
I get your frustration @skizzo. If its so clear then why is it so hard for anyone to say it. Thanks for posting your solution im sure it will help someone. Feel bad for not posting what I did to resolve this now.
@skizzo Thanks man you are the life saver ❤
How can we handle errors by ourselves completely?
I get this error below:
If I remove these lines:
Then, when we try to catch to the error through a middleware:
We get the same error back:
We are trying to make our custom error in the JSON format, for example:
{"status":404,"message":"page not found"}
Any ideas?