Laravel - 404 error handling for different route groups - laravel

I want to show two types of 404 error screen for users, authenticated users with admin rights inside the route /admin see an error page style and unauthenticated guests in the route/see another error page, how can I do this?

You can use the Exception Handler's render() method. From the documentation,
The render method is responsible for converting a given exception into
an HTTP response that should be sent back to the browser.
Instead of returning same views for all users, you can add the authorization logic in the App\Exceptions\Handler calss:
/**
* 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 CustomException) {
if(isAdmin()) {
return response()->view('admin.errors.custom', [], 500);
}
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $e);
}

Related

Need to Handle Laravel Exception and, Logging details mismatch error

I have tried to handle the exceptions on Handler class in my laravel project
if(!env('APP_DEBUG', false)){
return view('errors.500');
} else {
return parent::render($request, $exception);
}
Errors are redirecting to my page. but in the login page user name or password mismatch also redirecting to that page.
in the login error need to redirect to the login page,not to the common error page. how can handle it?
i have using default laravel auth login.
this is my Handler.php file,
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
// Render well-known exceptions here
// Otherwise display internal error message
if(!env('APP_DEBUG', false)){
return view('errors.500');
} else {
return parent::render($request, $exception);
}
}
}
no need to check for 500 errors manually .If you want to show custom error message for 500 erorrs in production then publish default laravel views for errors
php artisan vendor:publish --tag=laravel-errors
This will generate views for errors in following path
resources/views/errors
To customize 500 erorrs .Edit following path
resources/views/errors/500.blade.php
Also make sure this will only show when
APP_DEBUG=false

Can't make assertions because exceptions are crashing my test in Laravel

I want to test the response when I try to send invalid data to the API.
/** #test */
public function request_schedule_with_invalid_bdate()
{
$response = $this->json('GET', '/api/schedule', [
'bdate' => 'thisaintadate',
]);
$response->assertStatus(422);
}
According to the documentation it should return a 422
If the validation rules pass, your code will keep executing normally; however, if validation fails, an exception will be thrown and the proper error response will automatically be sent back to the user. In the case of a traditional HTTP request, a redirect response will be generated, while a JSON response will be sent for AJAX requests.
Also,
When using the validate method during an AJAX request, Laravel ... generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.
It sounds like Laravel should automatically handle the exception thrown and proceed with sending the response.
However, running this in PHPUnit would just cause an error.
There was 1 error:
1) Tests\Feature\ScheduleTest::request_schedule_with_invalid_bdate
Illuminate\Validation\ValidationException: The given data was invalid.
I read this question but using $this->expectException(...); will make the test pass but not run my assertions. If I assert the status to be something else other than 422, it will still pass.
My controller has this:
public function show(Request $request)
{
$attributes = $request->validate([
'bdate' => 'required|date'
]);
return ['data' => "It's valid."]
}
Here is my ExceptionHandler class (as requested by Martin H.) This is just what's out of the box. I haven't touched this class yet.
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
I have confirmed the following:
Laravel Framework 5.7.21 (php artisan --version output)
It runs the appropriate code/controller based on the stack trace:
.../vendor/laravel/framework/src/Illuminate/Validation/Validator.php:315
.../vendor/laravel/framework/src/Illuminate/Validation/Factory.php:136
.../vendor/laravel/framework/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php:53
.../vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php:108
.../app/Http/Controllers/MyController.php:35
What am I missing?
I was having a problem with the ValidationException crashing my test because I disabled exception handling. I disabled exception handling so I can debug my tests better (ironic, I know) and I forgot that I did this.
class ... extends TestCase
{
protected function setUp()
{
/**
* This disables the exception handling to display the stacktrace on the console
* the same way as it shown on the browser
*/
parent::setUp();
$this->withoutExceptionHandling();
}
Removing $this->withoutExceptionHandling(); now allows me to do assertions on the response.
There was 1 failure:
1) Tests\Feature\ScheduleTest::request_schedule_with_invalid_bdate
Expected status code 200 but received 422.
Failed asserting that false is true.
Relevant links:
- https://github.com/laravel/framework/issues/26013
- laravel phpunit withexceptionhandling

Laravel 5.6 - how to implement 404 page/route

I am trying to implement 404 page, but so far nothing is happening. I am getting this:
Not Found
The requested URL /test was not found on this server.
I have custom 404 page with completely different text.
In my routes file I have this route:
Route::fallback(function(){
return response()->view('errors/404', [], 404);
});
In Handler.php I added this:
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof MethodNotAllowedHttpException)
abort(404);
if ($this->isHttpException($exception)) {
if ($exception->getStatusCode() == 404) {
return response()->view('errors.404', [], 404);
}
}
return parent::render($request, $exception);
}
The 404.blade.php is located under the resources/view/errors
The 404.blade.php file should be located under resources/views/errors (note the 's' in views). And you don't need that custom code in your routes and Handler.php files, Laravel can handle 404's by itself.
The error message
Not Found
The requested URL /test was not found on this server.
is the default server 404 error message and not from Laravel.
You are supposed to see Laravel's default error page when you don't configure custom error pages.
This means that you might not have configured rewrite properly on your server and Laravel did not get the request.
You can check out this post on how to enable mod_rewrite on apache
in handler.php immport
use Illuminate\Session\TokenMismatchException;
and edit the render() function to following
public function render($request, Exception $exception)
{
if ($exception instanceof TokenMismatchException) {
if ($request->expectsJson()) {
return response()->json([
'dismiss' => __('Session expired due to inactivity. Please reload page'),
]);
}
else{
return redirect()->back()->with(['dismiss'=>__('Session expired due to inactivity. Please try again')]);
}
}
elseif($exception->getStatusCode()!=422){
return response()->view('errors.404');
}
return parent::render($request, $exception);
}
This is how you will be redirected to 404 page on any error.
TokenMismatchException is session expiration and status code 422 is validation error. Here $request->expectsJson() is for ajax json process

Override exception in handler

I am using Route model binding using the id of a product and my route looks like this:
Route::get('product/{product}', ProductController#index');
I also have a custom ProductNotFoundException which I want to return when the route model binding only for this model fails to get the row (the row does not exist or is soft deleted.)
Any ideas how I could achieve that?
The two solutions I thought of are:
Remove Route Model binding (obviously :P)
Override the exception in the Exceptions/Handler
I chose to go with the second and do the following
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
*
* #return \Symfony\Component\HttpFoundation\Response
* #throws ProductNotFoundException
*/
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException && $exception->getModel() === 'App\Models\Product') {
throw new ProductNotFoundException();
}
return parent::render($request, $exception);
}
However, I am not sure whether is a good practice to throw an exception in the exception handler.
Can anyone see any other way or advice of the above solution?
Customize the binding resolution in RouteServiceProvider:
Route::bind('product', function ($value) {
$product = Product::find($value);
if ($product === null) {
throw new ProductNotFoundException();
}
return $product;
});
Explicit Model Binding

get current request in 404 page

Hey I want to use the current request object as the facade not the static way($request not Request::) in a custom 404 blade file.
I don't know if I can hint about it to the error handler or is there a way to create that object?
Should/Could I do it via the Expections/Handler.php file?
I've found Here the following answer:
//Create a view and set this code in app/Exception/Handler.php :
/**
* Render an exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if($e instanceof NotFoundHttpException)
{
return response()->view('missing', [], 404);
}
return parent::render($request, $e);
}
//Set this use to get it working :
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
Is this the right way to do it?
Yes, you can do it from the Handler. Inside the render() method:
if ($e instanceof NotFoundHttpException) {
return response()->view('your.view.name', $dataYouWantToPass);
}

Resources