custom error handling without try/except in php Slim framework - php

Using php and the Slim Framework, is there a way I can set up the error-handler so my custom Exceptions that can automatically trigger the desired HTTP response, without forcing me to catch all the different exception types?
I know such examples from my projects with python Flask, but not the php equivalent.
For example, regardless where the exception is thrown in the code, I want my custom BadCustomerDataException() to trigger a HTTP 400 response, and the WaitingForResourceException() to trigger a 423 response, and the FaultyServerIsDeadAgainException() to trigger a 500 response.
Currently I'm using Slim version 3, with a planned update to version 4.

In Slim 4 you can add a custom error handler to the ErrorMiddleware. You can also add your own Middleware before the ErrorMiddleware to catch and map your own exceptions:
Example
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Exception\HttpNotFoundException;
use Slim\Middleware\ErrorMiddleware;
use Slim\Psr7\Response;
// ...
// HttpNotFound Middleware
$app->add(function (
ServerRequestInterface $request,
RequestHandlerInterface $handler
) {
try {
return $handler->handle($request);
} catch (HttpNotFoundException $httpException) {
$response = (new Response())->withStatus(404);
$response->getBody()->write('404 Not found');
return $response;
}
});
$app->add(ErrorMiddleware::class);
Source

Related

Implementation of Exception Handling in Laravel 5.2

I am trying to implement exception handling in my application. For this Laravel framework has its own mechanism to handle the exception using report and render method. But to implement exception I need to track the source from where the exception has been raised e.g. specific page, route etc. For this I need to pass the url to report and render method but unable to do so. What needs to be done in order to implement this in below report and render function.
public function report(Exception $e)
{
parent::report($e);
}
public function render($request, Exception $e)
{
/* Token mismatch Exception handler start */
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
return response()->view('errors.sessionExpire', [], 500);
}
/* Token mismatch Exception handler start */
return parent::render($request, $e);
}
As you can see from your own example, you have an instance of Request in the argument list. And Request has all request-specific details like current route, URL and so on.
$request->url(); // Current request URL
$request->fullUrl(); // With query parameters
$request->route(); // Get the route closure for this request path
You can also create your own exception classes that accept as many parameters as you wish!
And the less comfortable way already mentioned – you could go through the exception trace.
You need to use Exception::getTrace
var_dump($e->getTrace());
above line will give you all details regarding exception.
public function report(Exception $e){
echo '<pre>'; // add this line
print_r($e->getTrace()); // add this line
parent::report($e);
}

Unable to block error traces in a Symfony 3 controller

I am studying the Symfony\Component\Debug\Debug.
As far as I know the AppKernel's constructor can accept a second argument to define whether to use the debug modality or not (true/false).
What I actually don't understand is the usage and complementarity of Debug::enable() as it is indicated in the app_dev.php on the official Symfony Github's repository.
For example I tried to throw an Exception on a Controller in order to see the effect and I commented Debug::enable(); within app_dev.php but I always see the error page.
Why am I still seeing error traces in spite of commenting out Debug::enable();?
Short explanation
The Debug::enable() method registers a fallback error handler, which will be called if your application failed to handle an error.
The error page you see when kernel is booted with the $debug flag set to true, is a result of your application error handling (implemented by an exception listener). Set the flag to false to disable stack traces. If you're only after testing you can also disable error pages in development.
The page shown by the Debug component is not as nice as the one provided by the exception listener, but it's nicer than the PHP one.
Detailed explanation
The front controller calls your application kernel:
$kernel = new AppKernel('dev', true);
$response = $kernel->handle(Request::createFromGlobals());
The application kernel boots itself, creates the container and calls the http kernel to handle the request:
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (false === $this->booted) {
$this->boot();
}
return $this->getHttpKernel()->handle($request, $type, $catch);
}
The http kernel will use the event dispatcher to trigger certain events (kernel.request, kernel.response, kernel.exception etc). When an exception is thrown while handling the request, the http kernel will catch it and trigger the kernel.exception event:
// the following code is simplified to show the point
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
try {
return $this->handleRaw($request, $type);
} catch (\Exception $e) {
return $this->handleException($e, $request, $type);
}
}
private function handleException(\Exception $e, $request, $type)
{
$event = new GetResponseForExceptionEvent($this, $request, $type, $e);
$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
// ...
}
One of the listeners registered by default in the Symfony Standard Edition is the Symfony\Component\HttpKernel\EventListener\ExceptionListener. It's responsible for rendering nice error pages.
However, it will only handle exceptions thrown while handling a request in the http kernel. So if anything goes wrong outside of this call, it won't be handled (have a look at the catch blog in the previous code example).
This is where the Debug component comes in. The Debug::enable() method registers an error handler, an exception handler and a special class loader. You can use it in any PHP project without the http kernel. It is sort of a fallback error handler which will be called if your application failed to handle an error. It has no relation to the $debug constructor argument in the kernel.

Symfony2 catch PDO errors when booting Kernel

I am using Symfony 2.4 and am trying to create a more powerful exceptions handler that, on certain PDO / Doctrine exceptions, changes the status code of the response from 500 to 503 to display a different custom error message than our standard (in other words, it returns the error503.html.twig template rather than error500.html.twig). So far, I have created a custom Exceptions controller that extends the TwigBundle ExceptionController, I have changed the Twig exception parameter in config.yml, and I am able to catch any and all exceptions that are thrown once Symfony calls handle(...) in HttpKernel.php:185 (so it's really the second time that handle is called -- this time being on the HttpKernel rather than the AppKernel). I'll refrain from posting all that code, and instead direct the reader here to learn more about my method if they are unfamiliar. All of that code is working just fine -- I am able to modify any applications that are thrown within my application, so you can assume that I'm using the aforementioned approach properly.
The issue I am running into is that in addition to catching exceptions that are thrown within Symfony, I also want to also be able to catch exceptions that are thrown before the HttpKernel's handle method is called (an example being a PDO Access Denied exception that is thrown from improper database credentials). To give you a more specific rundown, in app_dev.php, you have:
$response = $kernel->handle($request);
which calls:
/**
* {#inheritdoc}
*
* #api
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (false === $this->booted) {
$this->boot();
}
return $this->getHttpKernel()->handle($request, $type, $catch);
}
Now, if an exception gets thrown in $this->boot(), it doesn't look like it gets caught anywhere, and because of that, I can't see any way of gracefully handling said exception in Symfony. It's only if the exception gets thrown within the try / catch block contained in $this->getHttpKernel()->handle($request, $type, $catch) that it will be caught and gracefully handled using Symfony code. Am I wrong about that? Does anyone know of an approach to handling exceptions that are thrown in this context that utilizes Symfony? My apologies in advance if this has already been answered elsewhere.
I ran into a similar problem, I didn't see a neat way around this but was able to get nice error pages for my specific problem simply by generating a Response object and sending that. I placed the following in some code which was called by boot()
try {
someExceptionFunction();
} catch (Exception $e) {
$response = new Response('<html><body>'.$e->getMessage().'</body></html>');
$response->send();
exit;
}
You could easily add some more logic to the catch block, catching different exceptions. It's not as clean/abstract as it could be, but since the entire framework fails to boot I don't know of any option you could use it to parse an error page.
Hope this helps

When running unit tests with laravel, how do you test your App::error() implementations?

I'm currently working on an open source personal project that provides a nice backend api for game developers. I'm in the early stages of development, but I plan to write tests as I go along, which is where I've hit a snag.
Through out the system when an error occurs such as incorrect api credentials or missing credentials, I throw a custom exception which stores a bit of extra data so that I can catch it and give a JSON encoded response.
The tests work fine for those thrown in my BaseController, but I also capture a few Laravel Exceptions so I can respond with my own, or at least, output JSON like below:
app/start/global.php
App::error(function(Exception $exception, $code) {
Log::error($exception);
});
App::missing(function(Exception $exception) {
return BaseController::error(
Config::get('response.method.code'),
Config::get('response.method.http'),
'Method not found'
);
});
App::error(function(Viper\Exception $exception) {
return BaseController::error(
$exception->getCode(),
$exception->getStatusCode(),
$exception->getMessage()
);
});
I'm using the try { } catch() { } approach as I need to check an extra value that isn't in the normal Exceptions.
public function testNoMethodGET() {
$config = Config::get('response.method');
try {
$this->call('GET', '/');
} catch(\Viper\Exception $e) {
$this->assertEquals($e->getCode(), $config['code']);
$this->assertEquals($e->getStatusCode(), $config['http']);
}
$this->fail('Exception not thrown');
}
This is all good and well, but I want to check a few things on the actual response, like for example, whether or not the json is valid, whether or not the response structure matches and whether or not the response values are correct.
If I set the return value of $this->call() to a variable, I'd be unable to access that variable within the catch block, so the question is this, how can I test the return value of $this->call() once the Exception has been caught?
According to Taylor Otwell:
"this can be solved by de-coupling your
test. You really want to test the handler and that the exception is
thrown totally separately anyways [sic] to isolate your tests. For
instance:
App::error(function(ErrorType $e)
{
App::make('ErrorTypeHandler')->handle($e);
});
Now you can write test cases for ErrorTypeHandler class separately
from the rest of your application. Then check that proper exceptions
are thrown by your app with #expectedException."
see How do you test your App::error implementations?
In your case, you already have isolated your error handler in BaseController::error(), so you can test the responses directly in separate unit tests, without the use of $this->call(). Instead, just call $response = BaseController::error() with the desired parameters and then inspect the response and apply relevant assertions.

Capture exceptions with a different handler for applications REST

The problem
I'm building a small application with Silex. It's divided between a REST application and a website. (two controllers, same app).
The website has installed its own custom error handler, which returns a user friendly html page. The problem is that in the part dedicated REST application, I should somehow handle exceptions to return type [json] and content different from the error handler's custom website.
With Symfony2
This argument can also be applied to Symfony2, I would like also possible solution for it!
A first solution for Silex
Wrap the methods in try-catch block in order to rethrowing the exception to handler.
$app->get('/api/show-list', function() use($app){
try {
$show = // db query, etc.
return $app->json(array('show' => $show), 200);
} catch (Exception $e) {
throw new MyException;
}
});
$app->error(function (MyException $e, $code) {
// error api
});
The issue is that if an exception is thrown out of my controllor the default error handler will be used. Some tips?
And with Symfony?
I have been using the following in my Silex RESTful app to return errors in json format:
$app->error(function (\Exception $e, $code) use($app) {
return $app->json(array("error" => $e->getMessage()),$code);
});
Not sure if this is the right way, but it works for me.
This is documented on the Silex site:
http://silex.sensiolabs.org/doc/usage.html#error-handlers
On Symfony2 you can use the ExceptionHandler. On the Exception you have the stack trace, so you can identify where it was thrown.
Also, in Symfony2 you can customize depending on the expected format. It's well explained in it's documentation.
For instance, if you replace the ExceptionController with one of yours, the third parameter shows the expected format:
Reference on where to change the ExceptionController
ExceptionController API

Categories