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);
}
Related
So when a user randomly types a URL on a route that exists, they get an error message:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
After doing some searching, all the posts I can find suggest to change the render function inside of App\Exceptions\Handler and change it to this:
public function render($request, Exception $exception)
{
if($exception instanceof \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException){
return abort('404');
}
return parent::render($request, $exception);
}
However, with the newer version of Laravel this no longer exists. One post mentioned to add this in routes\web.php:
Route::fallback( function () {
abort( 404 );
} );
This works fine but I'm not sure if this is the best approach/right place to have it? Is there are any other alternative way?
I have also attempted to change the register function inside of App\Exceptions\Handler to this per the Laravel Doc (https://laravel.com/docs/9.x/errors#rendering-exceptions):
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
but it does not work
In a newer version on Laravel, you can add
$this->renderable(function (Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException $e) {
// do something
});
this line inside a register method on a class \App\Exceptions\Handler
If you want to handle NotFoundException you should use
$this->renderable(function (Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e) {
// do something
});
You can find more detailed answer on Laravel documentation here:
https://laravel.com/docs/9.x/errors#rendering-exceptions
I am currently integrating some logic in App/Exceptions/Handler.php. I would like to be able to access the HTTP Status Code on the $exception variable:
public function report(Throwable $exception)
{
dd($exception->statusCode);
parent::report($exception);
}
However I get the following error:
ErrorException Undefined property: ErrorException::$statusCode
When I dd($exception) I get the following:
Symfony\Component\HttpKernel\Exception\NotFoundHttpException {#1214 ▼
-statusCode: 404
-headers: []
#message: ""
#code: 0
#file: "C:\Users\CEX\Documents\GitHub\unified\vendor\laravel\framework\src\Illuminate\Routing\AbstractRouteCollection.php"
#line: 43
trace: {▶}
}
How do I access the statusCode?
If you look at the source code of Symfony\Component\HttpKernel\Exception\NotFoundHttpException you will find that it extends Symfony\Component\HttpKernel\Exception\HttpException if you look at the declaration of the class you will see that $statusCode is private but it has a getter method
class HttpException extends \RuntimeException implements HttpExceptionInterface
{
private $statusCode;
private $headers;
public function __construct(int $statusCode, string $message = null, \Throwable $previous = null, array $headers = [], ?int $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
parent::__construct($message, $code, $previous);
}
public function getStatusCode()
{
return $this->statusCode;
}
//...
}
As such you simply need to do $exception->getStatusCode() to retrieve the status code (404 in your case) though you should do a check to make sure your throwable implements the HttpExceptionInterface because that might not always be the case and so the method would not exist and you would get a fatal error
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface) {
$code = $exception->getStatusCode();
}
follow #Tofandel answer, my practice is as follows,
Since this question is relatively new I presume you are suing Laravel version 7 or 8. According to the documentary of Error Handling, the correct page rendering method has now been as
public function render($request, Throwable $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
In order to use customised error view, you would have to tell Laravel what kind of Exception instance your thrown $exception is. To overwrite the internal error handling you would want to catch the thrown as a HTTP exception. For these versions the correct Class is as Symfony\Component\HttpKernel\Exception\. Therefore,
bring in the handling Class by
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
on the top of your app\Exceptions\Handler.php
modify the default render() method to something like follows in accordance to your scenario,
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
//
//
public function render($request, Throwable $exception)
{
// return parent::render($request, $exception);
if ($exception instanceof HttpExceptionInterface) {
if (env('APP_ENV') === 'production' && $exception->getStatusCode() == 404) {
return response()->view('errors.404', [], 404);
}
if (env('APP_ENV') === 'production' && $exception->getStatusCode() == 500) {
return response()->view('errors.500', [], 500);
}
}
return parent::render($request, $exception);
}
Check the docs here https://www.php.net/manual/en/exception.getcode.php
Try $exception->getCode()
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
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);
}
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