How to trigger laravel jobs failed() method when job fails? - php

Currently I'm calling task Report.php and generating report using generateReport() method. I have checked that the jobs have been executed using CLI command php artsan queue:listen. If anything goes wrong while calling Artisan::call() the error message will be displayed in terminal. So I want to catch the exception in failed() and I want to log the error into logs.
I have tried with try catch in handle() method but it's not catching the exception.
protected $options;
public function __construct($options)
{
$this->options = array_merge(
[
'task' => 'Report',
'do' => 'generateReport',
'limit' => '10000'
],
$options
);
}
public function handle()
{
Artisan::call('execute', [
'--task' => $this->options['task'],
'--do' => $this->options['do'],
'--parameters' => $this->options,
]);
}
public function failed()
{
//
}
How can I trigger the failed() and get the error into logs?

Artisan::call is actually just calling the execute console class so if you throw an Exception in there it should automatically end up in the failed method.
However, in 5.2 the Exception object is not passed to the failed method (this was added in 5.3).
Laravel 5.2
So if you need the Exception object passed to the failed method then you will need to do something like this in 5.2:
public function handle()
{
try {
Artisan::call('execute', [
'--task' => $this->options['task'],
'--do' => $this->options['do'],
'--parameters' => $this->options,
]);
} catch (\Exception $e) {
$this->failed($e)
}
}
public function failed(\Exception $e = null)
{
//handle error
}
Laravel 5.3+
In 5.3 the Exception is automatically passed to failed so your code would be like this:
public function handle()
{
Artisan::call('execute', [
'--task' => $this->options['task'],
'--do' => $this->options['do'],
'--parameters' => $this->options,
]);
}
public function failed(\Exception $e = null)
{
//handle error
}

Related

render function in Handler.php not working Laravel 8

I want to return a JSON response instead of the default 404 error page when ModelNotFoundException occurs. To do this, I wrote the following code into app\Exceptions\Handler.php :
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException) {
return response()->json([
'error' => 'Resource not found'
], 404);
}
return parent::render($request, $exception);
}
However it doesn't work. When the ModelNotFoundException occurs, Laravel just shows a blank page. I find out that even declaring an empty render function in Handler.php makes Laravel display a blank page on ModelNotFoundException.
How can I fix this so it can return JSON/execute the logic inside the overriden render function?
In Laravel 8x, You need to Rendering Exceptions in register() method
use App\Exceptions\CustomException;
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->renderable(function (CustomException $e, $request) {
return response()->view('errors.custom', [], 500);
});
}
For ModelNotFoundException you can do it as below.
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
return response()->json(...);
});
}
By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering Closure for exceptions of a given type. You may accomplish this via the renderable method of your exception handler. Laravel will deduce what type of exception the Closure renders by examining the type-hint of the Closure:
More info about the error exception
This code doesn't work for me (in Laravel 8.74.0):
$this->renderable(function (ModelNotFoundException$e, $request) {
return response()->json(...);
});
Don't know why, but ModelNotFoundException is directly forwarded to NotFoundHttpException (which is a part of Symfony Component) that used by Laravel and will ultimately triggers a 404 HTTP response. My workaround is checking the getPrevious() method of the exception:
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
if ($e->getPrevious() instanceof ModelNotFoundException) {
return response()->json([
'status' => 204,
'message' => 'Data not found'
], 200);
}
return response()->json([
'status' => 404,
'message' => 'Target not found'
], 404);
}
});
And then we will know that this exception come from ModelNotFoundException and return a different response with NotFoundHttpException.
Edit
This is why ModelNotFoundException thrown as NotFoundHttpException
This one is my Handler file:
use Throwable;
public function render($request, Throwable $exception)
{
if( $request->is('api/*')){
if ($exception instanceof ModelNotFoundException) {
$model = strtolower(class_basename($exception->getModel()));
return response()->json([
'error' => 'Model not found'
], 404);
}
if ($exception instanceof NotFoundHttpException) {
return response()->json([
'error' => 'Resource not found'
], 404);
}
}
}
This one is only for all request in API route. If you want to catch all request, so remove the first if.
Please note that by default Laravel emits a JSON representation of an exception ONLY when you send a request with the header parameter Accept: application/json! For all other requests, Laravel sends normal HTML rendered output.

Change message of invalid scope

How can I change invalid scope(s) provided message in Laravel Passport
try to update this render function in app/Exceptions/Handler.php file
public function render($request, Exception $exception)
{
if ($exception instanceof \Laravel\Passport\Exceptions\MissingScopeException)
{
return response()->json(['message' => 'your message here']);
//abort(401);
}
return parent::render($request, $e);
}
You can catch that as an Exception like so in your controller:
try {
// Whatever you are doing which leads to such error.
} catch (MissingScopeException $e) {
return response()->json(['message' => 'YOUR DESIRED MESSAGE.']);
}
By the way, this is where leads to such exception.
I'm using laravel/framework v8.10.0 and laravel/passport v10.0.1.
If you are using Passport only for API you could change directly the throw exception in the Middleware.
Steps:
Go to app/Http/Kernel.php.
In the $routeMiddleware array you need to have the line:
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class.
Go to the CheckForAnyScope middleware with Ctrl + click (Windows) or Cmd + click (MacOS).
In the CheckForAnyScope middleware go to handle function and before the line:
throw new MissingScopeException($scopes); add your custom error response.
Example with code:
public function handle($request, $next, ...$scopes)
{
if (! $request->user() || ! $request->user()->token()) {
throw new AuthenticationException;
}
foreach ($scopes as $scope) {
if ($request->user()->tokenCan($scope)) {
return $next($request);
}
}
// Custom error response
return response()->json(['success' => false, 'errors' => ['This type of user cannot do this action.'], 'data' => null]);
// This line can be commented or deleted.
throw new MissingScopeException($scopes);
}

Exception while calling authenticate method of Zend Authentication Service

I'm migrating my ZF2 app to ZF3.
While calling the authenticate method, getting this exception
An error occurred
An error occurred during execution; please try again later.
No Exception available
This is how I' calling the method,
public function __construct($authService, $sessionManager, $config)
{
$this->authService = $authService;//Getting the Zend\Authentication\AuthenticationService object (no error here)
$this->sessionManager = $sessionManager;
$this->config = $config;
}
public function login($email, $password, $rememberMe)
{
if ($this->authService->getIdentity() != null) {
throw new \Exception('Already logged in');
}
// Authenticate with login/password.
$authAdapter = $this->authService->getAdapter();
$authAdapter->setEmail($email);//abc.gmail.com
$authAdapter->setPassword($password);//sha1 password
$this->authService->authenticate();//Exception is generating here
}
What is I'm doing wrong?
Your exception message is not enough,
you should check php_error.log for details.
I assume that you are not registered Auth Service in the config.
So in config/autoload/global.php add
'service_manager' => [
'invokables' => [
Zend\Authentication\AuthenticationService::class => Zend\Authentication\AuthenticationService::class,
]
],

form validation exception not catching by Exception in laravel 5.1?

In laravel5, I have catching all error at app/Exceptions/Handler#render function and it was working fine.
code given below,
public function render($request, Exception $e) {
$error_response['error'] = array(
'code' => NULL,
'message' => NULL,
'debug' => NULL
);
if ($e instanceof HttpException && $e->getStatusCode() == 422) {
$error_response['error']['code'] = 422;
$error_response['error']['message'] = $e->getMessage();
$error_response['error']['debug'] = null;
return new JsonResponse($error_response, 422);
}
}
return parent::render($request, $e);
}
But in laravel5.1,When form validation failes,it throws error message with 422exception. but it is not catching from app/Exceptions/Handler#render but working fine with abort(422).
How can I solve this?
You can catch simply by doing
public function render($request, Exception $e) {
if($e instanceof ValidationException) {
// Your code here
}
}
When Form Request fails to validate your data it fires the failedValidation(Validator $validator) method that throws HttpResponseException with a fresh Redirect Response, but not HttpException. This exception is caught via Laravel Router in its run(Request $request) method and that fetches the response and fires it. So you don't have any chance to handle it via your Exceptions Handler.
But if you want to change this behaviour you can overwrite failedValidation method in your Abstract Request or any other Request class and throw your own exception that you will handle in the Handler.
Or you can just overwrite response(array $errors) and create you own response that will be proceed by the Router automatically.

Laravel api class exception handling

I have a Laravel 4.2 application with the following route:
Route::group(['prefix' => 'api/v1'], function()
{
Route::resource('service_logs', 'ServiceLogsController', [
'only' => ['index', 'store', 'show']
]);
});
The ServiceLogsController controller extends from ApiController, which looks something like this cut down version:
class ApiController extends \BaseController {
protected $statusCode = 200;
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
public function respondInternalError($message = 'Internal Error!')
{
return $this->setStatusCode(500)->respondWithError($message);
}
public function respondWithError($message)
{
return Response::json([
'error' => [
'message' => $message,
'status_code' => $this->getStatusCode()
]
], $this->getStatusCode());
}
// ...
}
What I'd like to do is, when ever an un-caught exception occurs, I want to call the respondInternalError() method on my ApiController, so that the API consumer has a consistent response rather than nothing or html whoops error.
To achieve this, I tried adding the following code in my app/start/global.php
App::error(function(Exception $exception, $code)
{
Log::error($exception);
if (Request::is('api/*'))
{
App::make('ApiController')->respondInternalError('Uncaught api exception error occurred - ' . $exception->getMessage());
}
});
and to test it, I tried to make a POST request to the following url: /api/v1/service_logs/123.
this will not work, because that URL is a GET url, so Laravel throws the correct method not allowed exception. However, it's not getting caught.
Any idea how to implement a catch all exception handler per controller class basis?
Update Slightly improved working "global" api exception handler
App::error(function(Exception $exception, $code)
{
Log::error($exception);
if (Request::is('api/*'))
{
$errorName = Symfony\Component\HttpFoundation\Response::$statusTexts[$code];
return App::make('ApiController')
->setStatusCode($code)
->respondWithError($errorName . ' error has occurred.');
}
});
When an error occurs, you get this now (testing done in Chrome + Postman):
The solution is actually very very simple. You only need to return the return value of your controller function
if (Request::is('api/*'))
{
return App::make('ApiController')->respondInternalError('Uncaught api exception error occurred - ' . $exception->getMessage());
}

Categories