I am trying to handle PostTooLargeException in my Laravel 5.5 application.
When I trying to upload too big file through my form I receive PostTooLargeException which I successfully catch in app\Exceptions\Handler.php, but on that step I would like to redirect user back to the page with form and show an error message.
I wrote following code:
class Handler extends ExceptionHandler
{
...
public function render($request, Exception $exception)
{
...
if($exception instanceof PostTooLargeException){
return redirect()->back()->withErrors("Size of attached file should be less ".ini_get("upload_max_filesize")."B", 'addNote');
}
...
}
}
As a result I was redirected to the proper page but without any message and ViewErrorBag was empty.
Did I something wrong with that redirection?
Thank you a help!
The ViewErrorBag is empty because session is not start in the Handler. Solution was previously described by #Talinon at Laracast
To make session available in the Handler class, I moved \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class from $middleware to $middlewareGroups array at the App/Http/Kernel.php
My updated $middlewareGroups array looks like:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, // <<< this line was added
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
...
];
Related
hello i am new at laravel and i build an eCommerce platform (details aren't important)
anyway my problem is i created a route that can catch all event from paypal webhook but when i directly access to it it's working but when i try from paypal webhook Simulator or even if did a sandbox payment didn't go through
i know the problem is from csrf verification and i tried excluding the route but didn't work and also tried creating a new RouteServiceProvider
here is my code in the Controller so i can catch anything from the request
$headers = getallheaders();
file_put_contents("/home/username/public_html/test.txt", json_encode($headers));
here is my route
Route::domain(env("APP_DOMAIN"))->group(function () {
Route::get('/paypal/n', 'HomeController#notifications');
});
i used domain(env("APP_DOMAIN")) because everyone can add his own domain and i want this to work just in the main domain .
the code in the RouteServiceProvider
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapPaymentRoutes();
//
}
protected function mapPaymentRoutes()
{
Route::middleware('payment')
->namespace($this->namespace)
->group(base_path('routes/payment.php'));
}
and of course i did define the payment middleware in the file Kernel.php and comment the VerifyCsrfToken class
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'payment' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// \App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
and even with all this and like a lot of test i couldn't get it to work
if i send a request to a pure php file it's working fine.
can you please help me with this i tried to find a solution in my own but it takes me like 15 days without any luck
i use laravel 6 .
in case someone else is struggling with this problem i had to do a work a round by creating a pure php file in public and send webhook request to that file url/file.php and the file catch data and send it to a command i created inside laravel to process it, so that everything that has anything to do with database is inside laravel and the file.php is just like a pipe .
i know it's not the best idea but it's not the worst either
and thanks for everyone who tried to help me .
everybody.
I made a middleware. I made a global one putting it into Kernel.php, which is called to every request and which verify if user is loged in.
The point was I needed to use the session for that. But the session was empty. Probably because it is populated later in script.
So I fix it using a global middleware called '\Illuminate\Session\Middleware\StartSession::class'. It was great because after that I could see, in my middleware, what session contains.
But another bug showed up. Since I puted the '\Illuminate\Session\Middleware\StartSession::class' middleware, my redirects doesn't put on session the variable anymore.
Till now code below worked perfectly and redirect created the arsh variable on session:
return redirect('/admin')->with('arsh', $arsh);
But now doesn't put anymore the arsh variable on session on redirect.
I researched a lot on internet, but nothing. I saw a lot of advices but no one worked.
I just don't know what I can do. If you know something would be awesome.
You also can think about using another solving method for reading session in my middleware, and probably this won't need anymore thinking about last bug.
I hope you understood what I wrote and sorry for my english.
Edit:
I did what Hamoud said:
I moved my middleware (\App\Http\Middleware\RedirectIfNotAuthenticated::class,) from $middleware to $middlewareGroups:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrimStrings::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\RedirectIfNotAuthenticated::class, // my middleware
],
'api' => [
'throttle:60,1',
'bindings',
],
];
And now redirect works well and can sets variables on session. But my middleware doesn't work anymore:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Session;
use Request;
class RedirectIfNotAuthenticated
{
/**
* handle
*/
public function handle($request, Closure $next, $guard = null)
{
if ($request->is('admin/*') && !$request->is('admin') && !$request->is('admin/login') && (!Session::has(_DB_PREFIX_.'id_admin') || !Session::has(_DB_PREFIX_.'name_admin'))) {
return redirect('/admin');
}
return $next($request);
}
}
If statement is never true...
That because $middleware is called before verifying the route, while $middlewareGroups is called after. So I am redirect to 404 when my url is something like domain.com/admin/fgtsdr.
What my middleware does is if route is admin/* and I am not loged in, it redirects me to /admin. The point is it has to do that even if route exists or not...
You did not post your Kernel.php file, and it's not clear what do mean by global middleware. Do you add it the $middleware array or $middlewareGroups array?
The order of these middleware matters. In the Kernel.php file there are three arrays in which you can register your middleware.
$middleware. To be triggered with every request.
$middlewareGroups. You register group of middleware to assign to specific route. It has two main groups: web and api. The web group is assigned to all routes registered in the routes/web.php file.
$routeMiddleware. Individual middleware to assign to specific route.
I assume you started by putting your custom middleware in the $middleware to make it global, and when this did not work you added Laravel's StartSession middleware to the same array. However, StartSession is already registered in the $middlewareGroups array within the web group. So, you have two sessions one of them is destroying the other.
The correct way to add a middleware to all routes, when they need a session (web routes), is to add it in the web group if the $middlewareGroups after the StartSession middleware.
For example,
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class, <---- Laravel session middleware
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\CustomMiddleware::class, <--- Your global middleware.
],
Good afternoon guys, I'm having an issue I don't know if you have the same trouble, I upgrade my Laravel project and now all routes like this
Route::get('detail/client/{client}', "controller#method")
are breaking everything because the object instanced in the controller comes empty...
public function detail(FileRequest $request, Client $client){
dd($client) // empty object
}
If someone can help me with this please. If I remove the Client model and make the dd then return the ID of the object i.e "594"
You haven't written base Laravel version you are upgrading from, but I think it might have something in common with \Illuminate\Routing\Middleware\SubstituteBindings::class middleware.
Make sure you have it in middlwareGroups like this:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class, // <- this is the line you should have
],
'api' => [
'throttle:60,1',
'bindings',
],
];
in app/Http/Kernel.php file
and also make sure the routes you have problem are in web middleware group.
This is driving me crazy for weeks now:
How can I make sessions available on the 404 page? I just
embed the 404 error page in my default template. It also shows the navbar
and the footer but how can I keep my user logged in when on a 404?
On a 404 Auth::check() always returns false and every else whats session specific is null or empty.
How to enable sessions on (404) error pages?
What you can do is, inside the app/http/Kernel.php add the follwing block of code:
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
Inside the $middleware variable. So it would look like:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
It worked for me, I hope it works for you as well.
So I know this is an old question but in case anyone finds it useful here's how I've dealt with this:
In my App/Exceptions/Handler.php I changed the default render method to:
public function render($request, Exception $exception) {
\Route::any(request()->path(), function () use ($exception, $request) {
return parent::render($request, $exception);
})->middleware('web');
return app()->make(Kernel::class)->handle($request);
}
This achieves 2 requirements:
All the web middleware run correctly for the request
Valid API routes do not start the session
This is in practice a decision to make the web middleware group the default to run when there's an error. Laravel would normally not run any middleware group on error pages. You can of course specify additional middleware or other route parameters if you want or use other conditions like e.g. if the path starts with api/ then use the api middleware instead making it more consistent.
Hopefully this is helpful to someone.
For Laravel 5.5.5 or above just use the Route::fallback:
Route::fallback('PagesController#notFound');
this will help you to customize your 404 views having access to all the session, and more, stuff.
Just to expand a bit on the existing answer: make sure to remove that middleware from $middlewareGroups if it's also there, so you don't apply the middleware twice.
You'll end up with something like this:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
//\Illuminate\Session\Middleware\StartSession::class,
//\Illuminate\Session\Middleware\AuthenticateSession::class,
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
As far as my understanding goes this was caused because the middleware that deals with the session, by being at the web group was being applied only to those pages routed on web.php. And since the default error handling does not redirect to a routed page we had no access to the session.
This way the middleware will be applied to all pages, and not only to those routed on web.php, including the error ones.
I originally found the soution here, but took me a while to understand why this was happening (thought i cold have gotten it all wrong, feel free to confirm or correct this please).
Hope it helps, it's working for me on Laravel 5.4
To avoid problems when you have other exception or recive request with api middleware,change the render function In App/Exceptions/Handler.php to :
public function render ($request, Exception $exception)
{
if ($this->isHttpException($exception)) {
switch ($exception->getStatusCode()) {
case '404':
\Route::any(request()->path(), function () use ($exception, $request) {
return parent::render($request, $exception);
})->middleware('web');
return app()->make(Kernel::class)->handle($request);
break;
default:
return $this->renderHttpException($exception);
break;
}
} else {
return parent::render($request, $exception);
}
}
I don't really see how any of these answers are great. They are over complicated and tend to suggest to rebuild the kernel with an additional route, causing a spike in memory and cpu usage just to make a 404 page.
I suggest just creating the route you need, and not using the automatic error pages that the Laravel pages suggest. In newer versions of Laravel this has been improved, but for this version I just recommend the following:
routes.php
Route::group(['middleware' => ['web']], function ($router) {
$router->get('/404', [
'uses' => 'HttpErrorController#404',
'as' => 'errors.404',
]);
});
Obviously depending on how you are set up, this is how I'd use the routes and create a HttpErrorController to handle the route, this why it's already in the web middleware group
App\Exceptions\Handler.php
public function render($request, Exception $exception)
{
if ($this->isHttpException($exception) && $exception->getStatusCode() === 404)
{
return redirect()->route('404');
}
return parent::render($request, $exception);
}
You can handle these any way you see fit, add a switch in there, check if it's a json request and then handle that differently etc
I've a payment system, where data is submitted to 3rd party site and than hauled back...
When data returns it hits specific url lets say /ok route. $_REQUEST['transaction'].
But because of laravel middleware I'm getting token mismatch. There is no way 3rd party payment API can generate token, so how I disable it? only for this route?
or is there a better option?
Route::get('/payment/ok', 'TransactionsController#Ok');
Route::get('/payment/fail', 'TransactionsController#Fail');
public function Ok( Request $request )
{
$transId = $request->get('trans_id');
if ( isset( $transId ) )
{
return $transId;
}
}
Since version 5.1 Laravel's VerifyCsrfToken middleware allows to specify routes, that are excluded from CSRF validation. In order to achieve that, you need to add the routes to $except array in your App\Http\Middleware\VerifyCsrfToken.php class:
<?php namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
'payment/*',
];
}
See the docs for more information.
Since Laravel 7.7 you can use method withoutMiddleware eg:
Route::get('/payment/ok', 'TransactionsController#Ok')
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
Route::get('/payment/fail', 'TransactionsController#Fail')
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
The technique described by #jedrzej.kurylo works well for excluding one or two pages.
Here's a different technique if you need to exclude lots of pages from CSRF validation, with more future-proofing.
You can segment your routes, and apply different middleware to each. So you can put your payment routes into a separate route groups, and not apply VerifyCsrfToken to them. Here's how.
1. Create a routes file
You'll notice in your routes directory, you have the following tree:
routes/
routes/api.php
routes/web.php
Create a new file here, routes/payment.php, and add your routes above to it:
<?php
use Illuminate\Support\Facades\Route;
Route::get('/payment/ok', 'TransactionsController#Ok');
Route::get('/payment/fail', 'TransactionsController#Fail');
2. Process the route with the RouteServiceProvider
In Laravel, Routes are processed by app\Providers\RouteServiceProvider.php. You'll notice these functions: map() and mapWebRoutes(). Add to this file accordingly (I've excluded the stock comments for brevity).
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapPaymentRoutes(); // <---- add this line
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapPaymentRoutes() // <--- Add this method
{
Route::middleware('payment') // <--- this line is important
->namespace($this->namespace)
->group(base_path('routes/payment.php'));
}
Notice we've added a new middleware layer. This is important for the next step.
3. Add a new middleware layer
Your middleware for your route groups are defined in App\Http\Kernel.php.
Update the $middlewareGroups property, and add a middle entry for 'payment'. It can be exactly the same as web, but without the VerifyCsrfToken line.
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\NoClickjack::class,
\App\Http\Middleware\SecureReferrerPolicy::class,
\App\Http\Middleware\NoXssScripting::class,
],
// ********** Add this *******************
'payment' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// This is the line you want to comment-out / remove
// \App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\NoClickjack::class,
\App\Http\Middleware\SecureReferrerPolicy::class,
\App\Http\Middleware\NoXssScripting::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
🎉
Now whenever you add new routes that need to be excluded from the CSRF Token check, add them to the routes/payment.php file.