Replacement for notFoundHandler setting - php

I'm migrating from Slim/3 to Slim/4. I've found or figured out replacements for all the features I was using that have been removed, except 404 Not Found Handler (part of the now gone App::$settings):
Slim App::$settings have been removed, multiple middleware have been implemented to replace the functionality from each individual settings.
Is there a middleware for notFoundHandler? If there isn't, how can I implement it?
Mine used to look like this:
use Slim\Container;
$config = new Container();
$config['notFoundHandler'] = function (Container $c) {
return function (Request $request, Response $response) use ($c): Response {
$page = new Alvaro\Pages\Error($c);
return $page->notFound404($request, $response);
};
};

According to Slim 4 documentation on error handling
Each Slim Framework application has an error handler that receives all
uncaught PHP exceptions
You can set a custom error handler to handle each type of exceptions thrown. A list of predefined exception classes is available on same page.
Here is a very basic example of how to register a closure as an error handler, to handle only HttpNotFoundException exceptions. You can also put the handler in a class that extends Slim\Handlers\ErrorHandler. Also, I did not actually use your Alvaro\Pages\Error to generate the response, but changing it should be straightforward:
<?php
require '../vendor/autoload.php';
$app = Slim\Factory\AppFactory::create();
// Define Custom Error Handler
$customErrorHandler = function (
Psr\Http\Message\ServerRequestInterface $request,
\Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app) {
$response = $app->getResponseFactory()->createResponse();
// seems the followin can be replaced by your custom response
// $page = new Alvaro\Pages\Error($c);
// return $page->notFound404($request, $response);
$response->getBody()->write('not found');
return $response->withStatus(404);
};
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
// Register the handler to handle only HttpNotFoundException
// Changing the first parameter registers the error handler for other types of exceptions
$errorMiddleware->setErrorHandler(Slim\Exception\HttpNotFoundException::class, $customErrorHandler);
$app->get('/', function ($request, $response) {
$response->getBody()->write('Hello Slim 4');
return $response;
});
$app->run();
Another approach is to create a generic error handler and register it as the default handler, and inside that handler, decide what response should be sent based on type of exception that is thrown. Something like:
$customErrorHandler = function (
Psr\Http\Message\ServerRequestInterface $request,
\Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app) {
$response = $app->getResponseFactory()->createResponse();
if ($exception instanceof HttpNotFoundException) {
$message = 'not found';
$code = 404;
} elseif ($exception instanceof HttpMethodNotAllowedException) {
$message = 'not allowed';
$code = 403;
}
// ...other status codes, messages, or generally other responses for other types of exceptions
$response->getBody()->write($message);
return $response->withStatus($code);
};
Then you can set this as the default error handler:
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);

Related

Get ValidationException in customer Middelware after the Response [response->exception = null]

I want to modify the Errors fields when I have ValidationException. The problem is when I have ValidationException, the field exception of response variable give me null.
Part of handle function
dd($response);
if(isset($response->exception) && $response->exception instanceof validationException){
$data = $response->getData();
$transformedErros = array();
foreach ($data->error as $field => $value) {
$transformedField = $transformer::transformedAttribute($field);
$transformedErros[$transformedField] = str_replace($field, $transformedField, $value);
}
$data->error = $transformedErros;
$response->setData($data);
}
It's probably because the exception handler already has handled the exception.
In any case. You error handling should not live in your middleware but in your exception handler. You can register your own exception handler in bootstrap/app.php with below code:
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
Let App\Exceptions\Handler extend \Laravel\Lumen\Exceptions\Handler and override what you need to change.
Take a look at https://laravel.com/docs/5.8/errors#the-exception-handler to learn more about exception handlers.

I can't add sentry to php slim

I need to add php sentry error handler to my slim 3 project.
how can I do so ?
where should put sentry integration code?
what I'm doing now is :
// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
$client = new Raven_Client(
'http://key#ip:9000/2'
);
$handler = new Monolog\Handler\RavenHandler($client);
$handler->setFormatter(new Monolog\Formatter\LineFormatter("%message% %context% %extra%\n"));
$logger->pushHandler($handler);
return $logger;
};
but I'm not getting all errors in my sentry dashboard.
for example accessing undefined array indexes.
thanks.
I think the best way is to just do the following (I did not test this or have ever used Slim but looking at the Slim docs this is a way to do it):
In your index.php (which should be the app entrypoint) just after require '../../vendor/autoload.php'; (the composer autoload).
Add the Raven initialization code:
$sentry = new Raven_Client('http://key#ip:9000/2');
$sentry->install();
This will configure the SDK to handle (and send) all errors, no need for the Monolog handler anymore.
If you want to integration it in a ErrorHandler class you created looking at this skeleton project might give you some ideas.
I am using a custom error handler to catch exceptions. This way i can use the default slim error handler and Sentry error reporting at the same time.
This is my code:
// initalize sentry
Sentry\init(['dsn' => 'your_dsn' ]);
// Run app
$app = (new App())->get();
// register custom error handler
$c = $app->getContainer();
$c['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
// send error to sentry
Sentry\captureException($exception);
// invoke default error handler
$handler = new Slim\Handlers\Error();
return $handler->__invoke($request, $response, $exception);
};
};
$app->run();
Not sure if this is the "recommended" way, but it works.

Laravel custom exception

Before posting this question I have searched internet for appropriate answers but got none.These are my following questions:
1) How to throw exception without try catch in laravel controller and get exception in the view of the called controller.
Example : TestController.php
function index(){
throw new CustomException('Data not found');
return view('dashboard');
}
How to get exception message in dashboard view
2) How to set format for exception message, suppose I want to return format as
$response['code'] = 0;
$response['status'] = 'error';
$response['message'] = 'message';
$response['data'] = '';
I have created a custom exception but don't know how to use it to fully
<?php
namespace App\Exceptions;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Mockery\Exception;
class CustomException extends Exception{
public $response;
/**
* Report the exception.
*
* #return void
*/
public function report()
{
}
/**
* Render the exception into an HTTP response.
*
* #param \Illuminate\Http\Request
* #return \Illuminate\Http\Response
*/
public function render($request,$exception){
ob_clean();
$response['code'] = 0;
$response['status'] = 'error';
$response['message'] = 'Message';
$response['data'] = '';
if(!$request->ajax()){
// non ajax response
}else{
return response()->json($response);
}
}
}
Answering your questions:
To pass this exception to view, you can implement render method what you already started to do. You can just do:
if(!$request->ajax()){
view('error_handler', compact('exception'))
} else {
return response()->json($response);
}
so now you can just create error_handler.blade.php view and you will have access in there to $exception variable, so you can use in there {{ $exception->getMessage}} and so on
You didn't define what exactly you want to achieve, however it should work without any problem:
public function render($request,$exception) {
if(!$request->ajax()){
view('error_handler', compact('exception'))
}
return response()->json([
'code' => $exception->getCode(),
'status' => 'error',
'message' => $exception->getMessage(),
'data' => 'sample data'
]);
}
Of course instead of using $exception->getCode() for code you can put anything you want, this is just an example that you can also use code and message that you have in your exception assuming you set some custom when you throw exception for example:
throw new CustomException('This is custom message', 123);
There isn't realy reason to thrown an exception if your purpose is to show the message of that exception within the view which is rendered by the controller. And It isn't a best way to manage exception, because by default all exception which are throw are handle and catch within the App\Exceptions\Handler class.
I think you know exactly when that type of CustomExption you have create will be thrown, but instead of throwing that error just traite that error which need an exception in different way without exception. For that you can create an Array in which old code, status, message, data and pass it to the view method;
class CustomController extends Controller
{
public function samemethod(){
// some code that can generate an error
// construct the error data
$customErrorData = [
'code' => 0000,
'status' => "some status",
'message' => "Some error message",
'data' => [...]
];
// After error data creation you can pass it to the view
return View::make('customview', compact('customErrorData'));
}
}
You have you error data in your view
All uncaught exceptions are intercepted by default exceptions handler. If you want this to behave differently, you just need to modify it: https://laravel.com/docs/5.7/errors#the-exception-handler

How can I intercept exceptions in ZendFramework 3

I'm using ZendFramework 3 in my REST API project. So there are few modules and a plugin which checks an authorization status. If the authorization fails it throws an Exception.
There is no way to handle it in each controller separately using try .. catch. How can I intercept and handle the Exception and generate JSON output like this?
{
message: "Access denied",
reason: "Your token is incorrect"
}
I'm a newbie in ZendFramework, that's why I have no idea how to do this. And official documentation didn't say a word about this.
There are default framework Events that are triggered including the event MvcEvent::EVENT_DISPATCH_ERROR. So, all you should do is to attach listener on that error event and return JSON response.
First, you need to register your Listener in module.config.php
// In my case module name is Api
'listeners' => [
Api\Listener\ApiListener::class // Register the class listener
],
'service_manager' => [
'invokables' => [
// Register the class (of course you can use Factory)
Api\Listener\ApiListener::class => Api\Listener\ApiListener::class
],
],
Second, create the file class Api/Listener/ApiListener.php
<?php
namespace Api\Listener;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
use Zend\Console\Request as ConsoleRequest;
use Zend\View\Model\JsonModel;
class ApiListener extends AbstractListenerAggregate
{
public function attach(EventManagerInterface $events, $priority = 1)
{
// Registr the method which will be triggered on error
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH_ERROR,
[$this, 'handleError'], 0);
}
/**
* Return JSON error on API URI(s)
*/
public function handleError(MvcEvent $e)
{
$request = $e->getParam('application')->getRequest();
if($request instanceof ConsoleRequest){
return;
}
//If you want to convert Response only on some URIs
//$uri = $request->getUri()->getPath();
//if(0 !== strpos($uri, '/api')){
// return;
//}
$response = $e->getResponse();
$exception = $e->getResult()->exception;
$errorType = $e->getError();
$errorCode = $exception && $exception->getCode() ? $exception->getCode() : 500;
$errorMsg = $exception ? $exception->getMessage() : $errorType;
$json = new JsonModel(['message' => $errorMsg]);
$json->setTerminal(true);
$response->setStatusCode($errorCode);
$e->setResult($json);
$e->setViewModel($json);
}
}
That's all. Now on every error, your custom logics will be executed.

Symfony: How to return a JSON response from a Before Filter (Listener)?

From following this example I have managed to set up the below Listener/Before Filter to parse all requests to my API endpoint (ie. /api/v1) before any controller actions are processed. This is used to validate the request type and to throw an error if certain conditions are not met.
I would like to differentiate the error response based on the applications environment state. If we are in development or testing, I simply want to throw the error encountered. Alternatively, if in production mode I want to return the error as a JSON response. Is this possible? If not, how could I go about something similar?
I'm using Symfony v3.1.*, if that makes any difference.
namespace AppBundle\EventListener;
use AppBundle\Interfaces\ApiAuthenticatedController;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class ApiBeforeListener
{
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Validates a request for API resources before serving content
*
* #param \Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
* #return mixed
*/
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller))
return;
if ($controller[0] instanceof ApiAuthenticatedController) {
$headers = $event->getRequest()->headers->all();
// only accept ajax requests
if(!isset($headers['x-requested-with'][0]) || strtolower($headers['x-requested-with'][0]) != 'xmlhttprequest') {
$error = new AccessDeniedHttpException('Unsupported request type.');
if (in_array($this->container->getParameter("kernel.environment"), ['dev', 'test'], true)) {
throw $error;
} else {
// return json response here for production environment
//
// For example:
//
// header('Content-Type: application/json');
//
// return json_encode([
// 'code' => $error->getCode(),
// 'status' => 'error',
// 'message' => $error->getMessage()
// ]);
}
}
}
}
}
Unlike most events, the FilterControllerEvent does not allow you to return a response object. Be nice if it did but oh well.
You have two basic choices.
The best one is to simply throw an exception and then add an exception listener. The exception listener can then return a JsonResponse based on the environment.
Another possibility to to create a controller which only returns your JsonResponse then use $event->setController($jsonErrorController) to point to it.
But I think throwing an exception is probably your best bet.
More details here: http://symfony.com/doc/current/reference/events.html

Categories