Laravel : How to handle Exception in View composer - php

In my laravel 5.5 project, view composers are used for passing data to the views.
In the view composer's constructor() a try catch block is used to catch the exceptions and a custom exception is rethrown from the catch method.
In the default exception handler of the application, custom exception is handled to display my custom error view.
Problem : The custom exception is not working properly when thrown from the view composer. Laravel's default exception error page is shown instead of my custom error page.
ProductComponentComposer.php
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Repositories\ProductRepository;
use Exception;
use App\Exceptions\AppCustomException;
class ProductComponentComposer
{
protected $products;
/**
* Create a new product partial composer.
*
* #param ProductRepository $productRepo
* #return void
*/
public function __construct(ProductRepository $productRepo)
{
try {
$this->products = $productRepo->getProducts();
} catch (Exception $e) {
throw new AppCustomException("CustomError", 1001);
}
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$view->with(['productsCombo' => $this->products]);
}
}
Handler.php
public function render($request, Exception $exception)
{
if($exception instanceof AppCustomException) {
//custom error page when custom exception is thrown
return response()->view('errors.app-custom-exception', compact('exception'));
}
return parent::render($request, $exception);
}
Note : The custom exception is handled properly if thrown from the controller.
I also tried throwing the exception from the compose() method of the ProductComponentComposer instead of the __constructor(). But that also not working.
How to fix this to get my custom exception view if any exception is occured in the view composer?
Thanks in advance..

I had the same issue where a custom exception was thrown within a method in my view composer class, yet \ErrorException is what I got displayed.
There's a handler on a framework's level (\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:45) I believe is causing this.
Fix I've applied:
App\Exceptions\Handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof AppCustomException ||
$exception instanceof \ErrorException &&
$exception->getPrevious() instanceof AppCustomException
) {
//custom error page when custom exception is thrown
return response()->view('errors.app-custom-exception', compact('exception'));
}
// default
return parent::render($request, $exception);
}
Make sure that what you're getting really is an instance of \ErrorException

Related

Laravel FatalErrorException but code seems ok

I'm dealing with laravel 5.0 throwing a FatalErrorException with no useful description.
We have a web app developed in laravel 5.0, with a small part in angularJS, and I work on an macbook air 2015.
A branch of the git project doesn't seem to work on my machine, although the other branches work just fine, and there's been no changes in configuration and no big changes in the code either. Also, it works on my colleague's machine.
Here's the returned error message:
local.ERROR: Symfony\Component\Debug\Exception\FatalErrorException:
Uncaught TypeError: Argument 1 passed to App\Exceptions\Handler::report() must be an instance of Exception,
instance of ParseError given, called in Applications/XAMPP/htdocs/myApp/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php on line 73 and defined in /Applications/XAMPP/htdocs/myApp/app/Exceptions/Handler.php:36
Here's Handler.php
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
use Flash;
use Response;
class Handler extends ExceptionHandler {
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
'Symfony\Component\HttpKernel\Exception\HttpException'
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* #param \Exception $e
* #return void
*/
public function report(Exception $e)
{
parent::report($e);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
if ($request->ajax()) {
return Response::json(null, 403);
}
$modelName = str_replace("No query results for model [App\Models\\", "", $e->getMessage());
$modelName = str_replace("].", "", $modelName);
Flash::error(trans(lcfirst($modelName) . '.not_found'));
return redirect()->back();
}
if ($e instanceof TokenBlacklistedException) {
return response()->json(['error' => 'token_blacklisted', 'description' => 'descrizione errore token blacklisted'], $e-> getStatusCode());
}
if ($e instanceof TokenInvalidException) {
return response()->json(['error' => 'token_invalid', 'description' => 'descrizione errore token invalid'], $e->getStatusCode()) ;
}
if ($e instanceof TokenExpiredException) {
return response()->json(['error' => 'token_expired', 'description' => 'descrizione errore token expired'], $e->getStatusCode()) ;
}
if ($this->isHttpException($e)) {
return $this->renderHttpException($e);
}
return parent::render($request, $e);
/*
if ($this->isHttpException($e))
{
return $this->renderHttpException($e);
}
else
{
return parent::render($request, $e);
}
*/
}
}
I'm used at seeing this type of generic message whenever I forget an import, but this doesn't seem to be case. I suspect I'm missing something in the configuration.
The error occurs on every page but the login page and the part in angular.
As soon as I log in and I get redirected to the homepage it crashes.
Any help would be appreciated.

Convert to Laravel exception?

Currently in a package, it has HttpException exception
namespace DigitalOceanV2\Exception;
class HttpException extends \RuntimeException implements ExceptionInterface
{
}
Is there a way to to convert it Laravel HttpResponseException uses without touching that exception from the package?
You can catch that exception and rethrow it.
In your app/Exceptions/Handler.php file.
public function render($request, Exception $exception)
{
if ($exception instanceof \DigitalOceanV2\Exception) {
throw new HttpResponseException;
}
return parent::render($request, $exception);
}
Edit: I haven't tested this but according to the exception class. You can pass a response as a parameter to the constructor. So you should be able to do this:
$response = response($exception->getMessage());
throw new HttpResponseException($response);
Maybe this is late, but at least on Laravel 8+ there is a \App\Exceptions\Handler::report method that I think would be more appropriate to override and put such logic - e.g. converting the exception to a different/custom class.
In fact render suggests how to render it, not what to do with it.
public function report(Throwable $e)
{
if ($e instanceof \DigitalOceanV2\Exception) {
throw new HttpResponseException;
}
return parent::report($e);
}

How to set custom exception handler in Laravel 5?

Hi I'm new to laravel and working with custom exception handling.
I have caught all exceptions known to my knowledge and it is working fine. As per my understanding, set_exception_handler is used for handling uncaught exceptions. Now I have two questions:
1) I have to know whether my understanding for set_exception_handler is correct or not.
2) How to implement it in laravel 5 to handle uncaught exceptions
This is how I have implemented set_exception_handler in my controller
class SearchController extends BaseController{
public function getTitleMessage($exc){
var_dump("set exception handler".$exc);
return json_encode("Error");
}
public function genericSearch(){
//Bussiness logic goes here
set_exception_handler('getTitleMessage');
throw new Exception("Search Failed");
}
This is showing an error that set_exception_handler is not a valid callback. So I have changed my code to
set_exception_handler(array($this,'getTitleMessage'));
But also not working for me. Someone guide me how to implement it in laravel controller. Thanks in advance
Laravel already uses a global exception handler
Take a look at the vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\HandleExceptions.php file; as you see in the bootstrap method, Laravel already uses set_exception_handler to set the handleException method as the global exception handler
That method will ultimately call App\Exceptions\Handler::render when an uncaught exception is raised.
So, if you want to handle in some way an exception that you're not catching manually, all you have to do is add your code to the render method:
app\Exceptions\Handler.php
public function render($request, Exception $e)
{
//DO WATHEVER YOU WANT WITH $e
return parent::render($request, $e);
}
You've to implement your custom exception handler logic in the app\Exceptions\Handler.php render method:
public function render($request, Exception $exception) {
if (method_exists($e, 'render') && $response = $e->render($request)){
return Router::prepareResponse($request, $response);
} elseif ($e instanceof Responsable) {
return $e->toResponse($request);
}
$e = $this->prepareException($e);
/* Your custom logic */
if ($e instanceof HttpResponseException) {
return $e->getResponse();
} elseif ($e instanceof AuthenticationException) {
return $this->unauthenticated($request, $e);
} elseif ($e instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($e, $request);
}
return parent::render($request, $exception);
}

Access Request Object in Error Handler Laravel

In my Laravel 5.2 project, I have a middleware happily storing requests and responses to DB or files.
There I serialize/json_encode $request object for logging everything going on. (cookies, input, files, headers...)
I need to create an error handler that will use whole request object to include everything about request into the report email. But ExceptionHandler::report() does not accept Request as a parameter.
Laravel 5.2 provides the helper method request(), which works for this use case:
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $exception)
{
$request = request();
parent::report($exception);
}
In App\Exceptions\Handler.php and the render method wich does have the request as parameter.
here you could fire an event to store the stuff in a session or database.
For example:
public function render($request, Exception $e)
{
if ($e instanceof HttpException) {
if ($e->getStatusCode() == 403) {
Event::fire(new UserNotAllowed($request));
return redirect()->to("/home");
}
if ($e->getStatusCode() == 404) {
if (Auth::guest()) {
return redirect()->to("/");
}
}
}
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
return parent::render($request, $e);
}
more info here.

Laravel Exceptions Handler not work

Environment: Laravel 5.1, PHP 5.6.10
I tried to implement App\Exceptions\Handler::render() to response error message in JSON format.
The app/Exceptions/Handler.php as follows:
// ignore..
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AbstractException) {
return response()->apiJsonError(
$e->getMessage(),
$e->getErrors(),
$e->statusCode());
}
// ignore...
}
In controller, it also throws an exception:
if (ArrayUtil::isIndexExceed($list, $maxIndex)) {
// Index exceeds
throw new App\Exceptions\ExceedingIndexException;
}
However, when the error occurs, the Handler::render() is not invoked. The response is the ExceedingIndexException stack.
The following part is Exception class
My custom exception class, ExceedingIndexException:
namespace App\Exceptions;
use App\Http\Responses\Error;
use App\Exceptions\AbstractException;
class ExceedingIndexException extends AbstractException
{
public function __construct()
{
$message = 'Unable to execute';
$error = new Error('exceeding_index_value');
$statusCode = 400;
parent::__construct($statusCode, $error, $message);
}
}
The ExceedingIndexException class inherits AbstractException:
namespace App\Exceptions;
abstract class AbstractException extends \Exception
{
protected $statusCode;
protected $errors;
public function __construct(
$statusCode, $errors, $message, $code = 0, \Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->statusCode = $statusCode;
$this->errors = $errors;
}
public function getStatusCode()
{
return $this->statusCode;
}
public function getErrors()
{
return $this->errors;
}
}
Solution
I found my project depends on Dingo API for RESTful API. Because, Dingo also supports and registers its own exception handler, App\Exceptions\Handler doesn't be invoked.
I tried to use Custom Exception Responses in Dingo API as my Exception Handler to response error in JSON format. And it works for me.
Actually, you can replace the Dingo API error handler by a custom error handler. Get a copy of https://github.com/KIVagant/api/blob/develop/src/Exception/Handler.php
and save it to YourApp\Exceptions\Api\V1\Handler.php. Add interface Dingo\Api\Contract\Debug\ExceptionHandler to it,
then follow the instruction in Exceptions now can return any additional data.
use Dingo\Api\Contract\Debug\ExceptionHandler as DingoExceptionHandler;
class Handler implements ExceptionHandler, DingoExceptionHandler {
Replace the error handler, eg in boot ().
// Resolve YourApp\Exceptions\Api\V1\Handler ifself
$this->app->alias('api.exception', 'YourApp\Exceptions\Api\V1\Handler');
$this->app->singleton('api.exception', function ($app) {
return new \YourApp\Exceptions\Api\V1\Handler($app['Illuminate\Contracts\Debug\ExceptionHandler'],
$app['config']['api.errorFormat'], $app['config']['api.debug']);
});
Don't forget set error format. You can set up error format in boot(), eg:
// Set up error format
$this->app['api.exception']->setErrorFormat(...)
Indeed, Dingo API took over the Exception handling.
You you face similar problems where Lumen Exception handler isn't handling anything, probably some package took over the handling. In this case it was Dingo API.
Register your custom response for the exception for Dingo API:
https://github.com/dingo/api/wiki/Errors-And-Error-Responses#custom-exception-responses

Categories