How to catch PostTooLargeException in Laravel? - php

To reproduce the error, simply upload a file(s) to any POST routes in Laravel that exceeds the post_max_size in your php.ini configuration.
My goal is to simply catch the error so I can inform the user that the file(s) he uploaded is too large. Say:
public function postUploadAvatar(Request $request)
try {
// Do something with $request->get('avatar')
// Maybe validate file, store, whatever.
} catch (PostTooLargeException $e) {
return 'File too large!';
}
}
The above code is in standard Laravel 5 (PSR-7). The problem with it is that the function can't execute once an error occurs on the injected request. Thereby can't catch it inside the function. So how to catch it then?

Laravel uses its ValidatePostSize middleware to check the post_max_size of the request and then throws the PostTooLargeException if the CONTENT_LENGTH of the request is too big. This means that the exception is thrown way before it even gets to your controller.
What you can do is use the render() method in your App\Exceptions\Handler e.g.
public function render($request, Exception $exception)
{
if ($exception instanceof PostTooLargeException) {
return response('File too large!', 422);
}
return parent::render($request, $exception);
}
Please note that you have to return a response from this method, you can't just return a string like you can from a controller method.
The above response is to replicate the return 'File too large!'; you have in the example in your question, you can obviously change this to be something else.
Hope this helps!

You can also redirect to a Laravel view of your choice if you wish to add a more content richer response to the user.
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Http\Exceptions\PostTooLargeException)
return response()->view('errors.post-too-large');
return parent::render($request, $exception);
}
ALSO NOTE:
For the exception to be caught you should make sure you provide the proper path to the PostTooLargeException by either importing the class using the use Illuminate\Http\Exceptions\PostTooLargeException; import statement or just writing the full path like in my example.

The exception is rendered before the Session starts so you cannot redirect back with an error message.
You can create a new blade file and redirect there like this:
public function render($request, Throwable $exception)
{
if ($exception instanceof \Illuminate\Http\Exceptions\PostTooLargeException) {
return $this->showCustomErrorPage();
}
return parent::render($request, $exception);
}
protected function showCustomErrorPage()
{
return view('errors.errors_page');
//you can also return a response like: "return response('File too large!', 422);"
}
To show a static message using sessions:
1.You have to move the ValidatePostSize class from the middleware array into the middlewareGroups array, directly after the StartSession.
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
After that in Handler.php you can do:
public function render($request, Throwable $exception)
{
if ($exception instanceof \Illuminate\Http\Exceptions\PostTooLargeException) {
return $this->showCustomErrorPage();
}
return parent::render($request, $exception);
}
protected function showCustomErrorPage()
{
return \Illuminate\Support\Facades\Redirect::back()->withErrors(['max_upload' => 'The Message']);
}
Show the message on your controller like this:
#error('max_upload')
<div class="alert" id="create-news-alert-image">{{ $message }}</div>
#enderror

Related

Prevent laravel message to be displayed and redirect the user to custom page issue in Laravel 5

I would like to catch somehow the laravel error, warning message. I don't want to disable them from the config/app.php file. I am using monolog to log some information.
This is my piece of code:
public function view($id){
try {
$tag = Tags::find(12313); // tags is a model
}catch(Exception $error){
echo 'error'; exit();
$this->log->logMessage(Logger::ERROR, $error->getMessage());
return redirect()->route('admin.tags')->with(['msg' => 'Smth went wrong']);
}
}
$this->log is a class where I am using the monolog class to log information.
The fact is that right now , it doesn't go to the catch part . I don't get the error message. I'm getting this message from laravel:
Trying to get property of non-object (View: ......
I intentionally put the number 12313 there to see if it is working or not. And for some reason is not working and I am not redirected . The idea, if something happened I want to redirect the user to a specific page with a general error message. How can I achieve that ?
You can do it in laravel .You can handle erors in App\Exceptions\Handler class
public function render($request, Exception $exception)
{
if($exception instanceof NotFoundHttpException)
{
return response()->view('errors.404', [], 404);
}
if ($exception instanceof MethodNotAllowedHttpException)
{
return response()->view('errors.405', [], 405);
}
if($exception instanceof MethodNotAllowedHttpException)
{
return response()->view('errors.404', [], 405);
}
return parent::render($request, $exception);
}
find() method doesn't throw an exception if the record is not found. So do this instead:
public function view($id)
{
$tag = Tags::find(12313); // tags is a model
if (is_null($tag)) {
$this->log->logMessage(Logger::ERROR, $error->getMessage());
return redirect()->route('admin.tags')->with(['msg' => 'Smth went wrong']);
}
}
Or use findOrFail() which will throw an exception if specified record is not found.
Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The findOrFail and firstOrFail methods will retrieve the first result of the query; however, if no result is found, a Illuminate\Database\Eloquent\ModelNotFoundException will be thrown

unit test render an exception into an HTTP response?

How to test if render() an exception into an HTTP response working correctly. I want to test without calling $this->get()
This is a method in Laravel:
public function render($request, Exception $e)
{
$response['exception'] = get_class($e);
$response['message'] = $e->getMessage();
if ($e instanceof LockException) {
return $this->errorResponse('lock', $response, 'Lock error has occurred', $e->getCode());
}
return parent::render($request, $e);
}
I need to test if LockException has turn into an HTTP response.
Something like this. In your test, instantiate the controller into a variable, create a blank request and pass in your LockException:
$response = $controller->render($request, $exception);
$this->assertEquals('string of HTML you expect', $response);

how to show 500 internal Server error page in laravel 5.2?

I want to show the page 500 internal server error Page. when user had syntax error mistake in project can anyone help me? if i do some mistake in syntax i want to show that particular blade.
You need to create handler to catching FatalErrorExceptions in your handler like below code:
Handler
In app/Exceptions/Handler.php
public function render($request, Exception $e)
{
// 404 page when a model is not found
if ($e instanceof ModelNotFoundException) {
return response()->view('errors.404', [], 404);
}
// custom error message
if ($e instanceof \ErrorException) {
return response()->view('errors.500', [], 500);
} else {
return parent::render($request, $e);
}
return parent::render($request, $e);
}
View
See resources/views/errors/500.blade.php. If not exist then create it.
You can get more detailed OR other ways from Laravel 5 custom error view for 500
In your resources/views/errors folder create a file named 500.blade.php.
Laravel makes it easy to display custom error pages for various HTTP
status codes. For example, if you wish to customize the error page for
500 HTTP status codes, create a resources/views/errors/500.blade.php. This file will be served on all 500 errors generated by your application.
The problem is that Laravel will only do this automatic rendering of error pages for exceptions that are instances of HttpException. Unfortunately when your server throws an error (method does not exist, variable undefined, etc) it actually throws a FatalErrorException. As such, it is uncaught, and trickles down to the SymfonyDisplayer() which either gives you the trace (debug true) or ugly one-liner 'Whoops, looks like something went wrong' (debug false).
To solve this you have add this to your render method to app/Exceptions/Handler
# /app/Exceptions/Handler.php
# use Symfony\Component\Debug\Exception\FlattenException;
# public function render($request, Exception $e)
$exception = FlattenException::create($e);
$statusCode = $exception->getStatusCode($exception);
if ($statusCode === 404 or $statusCode === 500) {
return response()->view('errors.' . $statusCode, [], $statusCode);
}
Docs
My solution is simple, just replace your render() method in Exceptions\Handler.php file with:
/**
* 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 ($request->expectsJson()) {
return $this->renderJson($request, $exception);
}
if ($this->shouldReport($exception) && app()->environment('production')) {
$exception = new HttpException(500, $exception->getMessage(), $exception);
}
return parent::render($request, $exception);
}
It will show 500 page if app in production environment. You will need to have 500.blade.php view in your resources/views/errors folder.
// app/Exceptions/Handler.php
protected function prepareResponse($request, Exception $e)
{
if($this->isHttpException($e) === false && config('app.debug') === false) {
$e = new HttpException(500);
}
return parent::prepareResponse($request, $e);
}
Like #Amit said
The problem is that Laravel will only do this automatic rendering of
error pages for exceptions that are instances of HttpException.
So my solution is to replace whatever exception that is not HttpException by a HttpException.
in app\Exceptions\Handler create the following method:
protected function convertExceptionToResponse(Exception $e)
{
$e = FlattenException::create($e);
return response()->view('errors.500', ['exception' => $e], $e->getStatusCode(), $e->getHeaders());
}
it will override the one in the parent class (Illuminate\Foundation\Exceptions\Handler) that displays the whoops page.
In Laravel 5.4, you could override prepareException function in your app\Exception\Handler.php:
/**
* #inheridoc
*/
protected function prepareException(Exception $e)
{
$exception = parent::prepareException($e);
if(!config('app.debug')) {
if(!$exception instanceof HttpException && $this->shouldReport($exception)) {
$exception = new HttpException(500);
}
}
return $exception;
}

Laravel : Handle findOrFail( ) on Fail

I am looking for something which can be like findOrDo(). Like do this when data not found. Something could be like
Model::findOrDo($id,function(){
return "Data not found";
});
Is there any similar thing in laravel that I can do this elegantly and beautifully ?
*I tried googling but could not find one
use Illuminate\Database\Eloquent\ModelNotFoundException;
// Will return a ModelNotFoundException if no user with that id
try
{
$user = User::findOrFail($id);
}
// catch(Exception $e) catch any exception
catch(ModelNotFoundException $e)
{
dd(get_class_methods($e)); // lists all available methods for exception object
dd($e);
}
Another option is to modify the default Laravel Exception Handler, found in app/Exceptions/Handler.php on the render() function I made this change:
public function render($request, Exception $e)
{
if(get_class($e) == "Illuminate\Database\Eloquent\ModelNotFoundException") {
return (new Response('Model not found', 400));
}
return parent::render($request, $e);
}
That way instead of getting a 500, I send back a 400 with a custom message without having to do a try catch on every single findOrFail()
By default, when you use an Eloquent model’s findOrFail in a Laravel 5 application and it fails, it returns the following error:
ModelNotFoundException in Builder.php line 129:
'No query results for model [App\Model]'.
So to catch the exception and display a custom 404 page with your error message like "Ooops"....
Open up the app/Exceptions/Handler.php file, and add the code shown below to the top of the render function:
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException)
{
abort(404, 'Oops...Not found!');
}
return parent::render($request, $e);
}
Source: https://selftaughtcoders.com/from-idea-to-launch/lesson-16/laravel-5-findorfail-modelnotfoundexception-show-404-error-page/
An alternative process could be to evaluate a collection instead. So,
$modelCollection = Model::where('id', $id)->get();
if(!$modelCollection->isEmpty()) {
doActions();
}
I agree it isn't as elegant, a one-liner or as case specific as you or I might like, but aside from writing a try catch statement every time, it's a nice alternative.
as of Laravel v5.7, you can do this (the retrieving single model variation of #thewizardguy answer)
// $model will be null if not found
$model = Model::where('id', $id)->first();
if($model) {
doActions();
}
A little later for the party, from laravel 5.4 onward, Eloquent Builder supports macros. So, I would write a macro (in a separate provider) like follows.
Builder::macro('firstOrElse', function($callback) {
$res = $this->get();
if($res->isEmpty()) {
$callback->call($this);
}
return $res->first();
});
I can then do a retrieval as follows.
$firstMatchingStudent = DB::table('students')->where('name', $name)
->firstOrElse(function() use ($name) {
throw new ModelNotFoundException("No student was found by the name $name");
});
This will assign the first object of the result set if it is not empty, otherwise will adapt the behaviour I pass into the macro as a closure (throw Illuminate\Database\Eloquent\ModelNotFoundException in this case).
For the case of a model also this would work, except that models always return the builder instance.
In the newer version of Laravel (for me in v9.41.0) we have the Register method in Handler.php ,
so for customize the exception of findOrFail method we have to add this code to that file:
$this->renderable(function (NotFoundHttpException $exception, $request) {
if ($request->expectsJson()) {
//when you need it for API
return response()->json([
'responseCode'=> 404,
'message' => "Item not found",
'errorCode' => 1000404
], 404 );
}else{
return view("some-custom-view");
}
});
I needed NotFoundHttpException which has the namespace below (you can use any other Exception for customization) and have to be used out of the class:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Show a 404 page if route not found in Laravel 5.1

I am trying to figure out to show 404 page not found if a route is not found. I followed many tutorials, but it doesn't work.
I have 404.blade.php in \laravel\resources\views\errors
Also in handler.php
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
// redirect to form an example of how i handle mine
return redirect($request->fullUrl())->with(
'csrf_error',
"Opps! Seems you couldn't submit form for a longtime. Please try again"
);
}
/*if ($e instanceof CustomException) {
return response()->view('errors.404', [], 500);
}*/
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException)
return response(view('error.404'), 404);
return parent::render($request, $e);
}
If I enter wrong URL in browser, it returns a blank page. I have
'debug' => env('APP_DEBUG', true),
in app.php.
Can anyone help me how to show a 404 page if route is not found? Thank you.
I recieved 500 errors instead of 404 errors. I solved the problem like this:
In the app/Exceptions/Handler.php file, there is a render function.
Replace the function with this function:
public function render($request, Exception $e)
{
if ($this->isHttpException($e)) {
switch ($e->getStatusCode()) {
// not authorized
case '403':
return \Response::view('errors.403',array(),403);
break;
// not found
case '404':
return \Response::view('errors.404',array(),404);
break;
// internal error
case '500':
return \Response::view('errors.500',array(),500);
break;
default:
return $this->renderHttpException($e);
break;
}
} else {
return parent::render($request, $e);
}
}
You can then use views that you save in views/errors/404.blade.php, and so on.
> The abort method will immediately raise an exception which will be rendered by the exception handler. Optionally, you may provide the response text:
abort(403, 'Unauthorized action.');
is your app_debug set to true? if that is the case, Laravel will throw the error with backtrace for debugging purposes, if you change the value to false, Laravel will show the default 404 page in the errors folder. That being said you can choose to use abort at any time you want. at the controller level or at the route level, it is totally up to you.
ie
Route::get('/page/not/found',function($closure){
// second parameter is optional.
abort(404,'Page not found');
abort(403);
});
#tester.Your problem has already been solved, try the command below in composer:
php artisan view:clear
Then try once more with an unknown URL. Because I have also faced the same error before.
There is no need for you to check the error type and manually render the 404 view. Laravel already knows to render the view with the HTTP error code that was thrown (404 = resources/views/errors/404.blade.php). Get rid of the extra check and it should work fine.
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
// redirect to form an example of how i handle mine
return redirect($request->fullUrl())->with(
'csrf_error',
"Opps! Seems you couldn't submit form for a longtime. Please try again"
);
}
return parent::render($request, $e);
}
I use the following in app/Exceptions/Handler.php (Laravel 5.2):
/**
* 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 \ReflectionException OR $e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) //Si la ruta no existe, mostar view 404.
return response(view('errors.404'), 404);
return parent::render($request, $e);
}
And it looks like this:img
In apache you could be able to put this code in .htaccess file at your main directory and make sure that change AllowOverride Directive to all in httpd confg file
ErrorDocument 404 the\path\to\404.blade.php
In config folder app.php change the following code
'debug' => env('APP_DEBUG', true),
In App->Exception->Handler.php Replace Render Function With Below Code
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException)
{
return response()->view('errors.404', [], 404);
}
if ($exception instanceof \ErrorException) {
return response()->view('errors.500', [], 500);
}
else {
return parent::render($request, $exception);
}
return parent::render($request, $exception);
}
If you want to redirect to the 404 page if the route is not found..
Route::fallback(function () {
return view('404');
});

Categories