Laravel Framework 8.48.0
PHP 7.4.18
Local Environment: Xampp, Windows10
I am developing RESTful API using Laravel 8. I have written some logic inside the register() method to make the error response better and more straightforward. Everything is working perfectly. But the problem is when Laravel detects an exception that doesn't fall any of this logic and APP_DEBUG=true, this piece of code start executing and takes too long to respond; it says execution timeout. If I clean the register() method, it shows an exception within 1 or 2 secs. What's wrong with my code?
I want a better and more precise error response + when APP_DEBUG=true quickly shows an error exception.
I think this is the culprit.
if (env('APP_DEBUG', false)) {
return parent::render($request, $exception);
}
Handler.php
class Handler extends ExceptionHandler
{
use ApiResponser;
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function register()
{
$this->renderable(function (Exception $exception, $request) {
if ($request->wantsJson()) {
if ($exception instanceof HttpException) {
$code = $exception->getStatusCode();
$message = Response::$statusTexts[$code];
return $this->errorResponse($message, $code);
}
if ($exception instanceof AuthenticationException) {
return $this->errorResponse($exception->getMessage(), Response::HTTP_UNAUTHORIZED);
}
if ($exception instanceof AuthorizationException) {
dd('authorization exception');
return $this->errorResponse($exception->getMessage(), Response::HTTP_FORBIDDEN);
}
if ($exception instanceof ValidationException) {
$errors = $exception->validator->errors()->getMessages();
return $this->errorResponse($errors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
if (env('APP_DEBUG', false)) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected error occurred. Please contact your administrator for help.', Response::HTTP_INTERNAL_SERVER_ERROR);
}
});
}
}
Related
I have a form when the user submits lostFound model. The validation request is as follows:
public function rules()
{
return [
'LFImage' => 'required|image|mimes:jpeg,jpg,png,gif,webp', // TODO: Read about 'Sometimes'.
'handoverStatement' => 'nullable|image|mimes:jpeg,jpg,png,gif,webp',
];
}
So, one attached is required (LFImage) and the other isn't (handoverStatement).
I created a dynamic image upload action:
class UploadImageAction implements UploadImageContract {
public function handle(Request $request, $image, $imageLocation)
{
$storedImage = ($request->hasFile($image))
? $request->file($image)->store($imageLocation)
: NULL;
if(!$storedImage)
{
throw new ImageUploadException('Something went wrong with the image upload.' . $storedImage);
}
return $storedImage;
}
}
Then calling it in the controller store() with try, catch:
// Add new Record.
public function store(LostFoundRequest $request, LostFoundService $lostFoundService, UploadImageContract $uploadImageAction)
{
try {
$LFImage = $uploadImageAction->handle($request, 'LFImage', 'lostFound/lostItems');
$handoverStatement = $uploadImageAction->handle($request, 'handoverStatement', 'lostFound/handoverStatements');
$lostFoundService->storeLostFound($request, $LFImage, $handoverStatement);
return redirect('data-entry/lost-and-found')->with('success', 'Item Added Successfully');
} catch (ImageUploadException $exception) {
// Handle upload image error
return back()->withErrors($exception->getMessage());
} catch (LostFoundException $exception) {
// Handle lostfound created error
return back()->withErrors('lostFound', $exception->getMessage());
} catch (\Throwable $exception) {
throw $exception;
}
}
Every time I submit the form without attaching handoverStatement I get the exception error (The data isn't saved, yet LFImage is uploaded to the dir).
What am I missing here?
Note: the ImageUploadException class doesn't have any methods:
<?php
namespace App\Exceptions;
use Exception;
class ImageUploadException extends Exception {}
Im trying intercept errors in laravel and i found a nice way to do that:
Simulating a error:
public function index(){
$users = User::all(); //<-SQL exeception here
return response()->json(['message'=>'ok'], 200);
}
app/Exceptions/Handler.php
public function report(Exception $exception)
{
dd($exception); //<-intercept my error here
parent::report($exception);
}
Works very well and i can do whatever i want with error, but when i use a try-catch block, my interceptor does not work:
Simulating error again
public function index(){
try {
$users = User::all();//<-SQL exeception here
} catch (\Throwable $th) {
error_log('Error handled');
//MyInterceptor::manuallyIntercept($th);
}
return response()->json(['message'=>'ok'], 200);
}
Is there a clean way to intercept all handled errors programatically?
Not report method, you need to use render method on Handler.php
You will see $this->errorResponse which is to just return JSON response. I just want to show the main idea.
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
}
if ($exception instanceof ModelNotFoundException) {
$modelName = strtolower(class_basename($exception->getModel()));
return $this->errorResponse("Does not exists any {$modelName} with the specified identificator", 404);
}
if ($exception instanceof AuthenticationException) {
return $this->unauthenticated($request, $exception);
}
if ($exception instanceof AuthorizationException) {
return $this->errorResponse($exception->getMessage(), 403);
}
if ($exception instanceof MethodNotAllowedHttpException) {
return $this->errorResponse('The specified method for the request is invalid', 405);
}
if ($exception instanceof NotFoundHttpException) {
return $this->errorResponse('The specified URL cannot be found', 404);
}
if ($exception instanceof HttpException) {
return $this->errorResponse($exception->getMessage(), $exception->getStatusCode());
}
if ($exception instanceof QueryException) {
$errorCode = $exception->errorInfo[1];
if ($errorCode == 1451) {
return $this->errorResponse('Cannot remove this resource permanently. It is related with any other resource', 409);
}
}
if (config('app.debug')) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected Exception. Try later', 500);
}
Error response method
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
guys. I am new to Laravel. Just installed 5.5 and try to catch the AuthenticationException in App\Exceptions\Handler like below
public function render($request, Exception $exception)
{
if ($exception instanceof AuthenticationException) {
//Do something
}
}
The problem is ($exception instanceof AuthenticationException) always return false.
dd($exception instanceof AuthenticationException) //return false.
When I dd($exception) I got
AuthenticationException{
#gurad...
....
.....
}
Then I try
get_class($exception) return \Illuminate\Auth\AuthenticationException
However,
dd($exception instanceof Exception) //return true.
Please help. Thanks.
You should make sure you use class from valid namespace:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
//Do something
}
return parent::render($request, $exception);
}
You mentioned:
dd($exception instanceof Exception) //return true.
That's true. Each exception class that will extend Exception class will return true for this, that's why in your handler you should make sure you first verify specific classes and not exception class, for example if you used:
public function render($request, Exception $exception)
{
if ($exception instanceof Exception) {
//Do something 1
}
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
//Do something 2
}
return parent::render($request, $exception);
}
always //Do something 1 would be launched first.
I am building an RESTful API in Laravel 5.2.
In my resource controllers I want to use implicit model binding to show resources. e.g.
public function show(User $users)
{
return $this->respond($this->userTransformer->transform($users));
}
When a request is made for a resource that doesn't exist Laravel automatically returns the NotFoundHttpException
NotFoundHttpException
I want to return my own custom response but how can I do that for a query that is done using route model binding?
Would something like this Dingo API response answer be able to be implemented?
Or will I stick with my old code which was something like this:
public function show($id)
{
$user = User::find($id);
if ( ! $user ) {
return $this->respondNotFound('User does not exist');
}
return $this->respond($this->userTransformer->transform($users));
}
So I could see if a resource (user) was not found and return an appropriate response.
See if you can catch ModelNotFound instead.
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
dd('model not found');
}
return parent::render($request, $e);
}
I think a good place would be in the Handler.php file under /app/Exceptions
public function render($request, Exception $e)
{
if ($e instanceof NotFoundHttpException) {
// return your custom response
}
return parent::render($request, $e);
}
In Laravel 7 and 8 you can do something like this.
In app/Exception/Handler.php class, add the render() method like below(if it doesn't exist).
Note that instead of type hinting Exception class you should use Throwable .
use Throwable;
public function render($request, Throwable $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
//For API (json)
if (request()->wantsJson()) {
return response()->json([
'message' => 'Record Not Found !!!'
], 404);
}
//Normal
return view('PATH TO YOUR ERROR PAGE');
}
return parent::render($request, $e);
}
I am writing a middleware in laravel 5. I want to throw a forbidden exception with code 403 from middleware. My middleware function is given below:
use Exception;
public function handle($request, Closure $next)
{
if (!Auth::check()) {
throw new Exception("Access denied", 403);
}
return $next($request);
}
I am calling my middleware from controller and I am getting error message with code 500 but not 403. How can I resolve this?
You can simply use the abort() helper. (Or App::abort())
public function handle($request, Closure $next) {
if (!Auth::check()) {
abort(403, 'Access denied');
}
return $next($request);
}
You can handle these exceptions inside App\Exceptions\Handler by overriding render() For example:
public function render($request, Exception $e)
{
if($e instanceof HttpException && $e->getStatusCode() == 403){
return new JsonResponse($e->getMessage(), 403);
}
return parent::render($request, $e);
}