tinyoverflow
tinyoverflow

Reputation: 2031

Laravel 5: Sessions on 404 route

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?

Upvotes: 9

Views: 5617

Answers (6)

Francisco Daniel
Francisco Daniel

Reputation: 1029

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.

Upvotes: 3

Chris Townsend
Chris Townsend

Reputation: 2716

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

Upvotes: 0

fatemeh sadeghi
fatemeh sadeghi

Reputation: 2573

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);
    }
}

Upvotes: 2

apokryfos
apokryfos

Reputation: 40653

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:

  1. All the web middleware run correctly for the request
  2. 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.

Upvotes: 5

lluchmk
lluchmk

Reputation: 440

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

Upvotes: 3

Kevuno
Kevuno

Reputation: 223

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.

Upvotes: 14

Related Questions